2017 ACM/ICPC Asia Regional Shenyang Online Ping Ping Ping 树链剖分+树状数组

来源:互联网 发布:英语课文朗读软件 编辑:程序博客网 时间:2024/05/22 07:08

原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6203

题目大意:给出n+1个节点的树( 3<n<10^4),并给出m对点(m<=50000),要求这m对点的最短链上至少有一个点背标记,问最少标记几个点。

先让我哭一会QAQ,昨天下午后半场一直在死磕这题,想了网络流,费用流,差分约束,树形DP就是没想到贪心,最后随便写了一发后效性明显地树形DP,假装没有划水(我好菜。。。。

其实这题可以稍微简化一下,假设只有一条链的情形。就是给出一条链和m对起始点和终点,要求这m对点之间至少有一个点被标记。

从左端点最接近左边界的一对点开始考虑,显然这对点一定要被选中,其次如果直接选中右端点,就可以同时标记最多的链。于是就有了一个贪心算法,每次选中左端点最靠左边界的未被标记的点对,标记这对点中的右端点。

然后回到这道题。对于被标记的点,都肯定有一个性质:是某个点对的LCA。这个可以反证法证明,如果有一个被标记点不是任意一对点的LCA,则不标记这个点,换做标记这个点往根节点遇到的第一个LCA点显然不会使得某个链的标记消失。

接着,对于深度最大的一个LCA,这个LCA肯定是要被标记的,因为不存在其他LCA在这个点对的链路上,于是就有了一个贪心算法,每次标记深度最大的,且其所在链路未被标记的LCA。

怎么知道某一条链路是否被标记过呢?这就很好处理了,只要用树链剖分来处理某个点是否被标记,同时用树状数组查询前缀和,可以得知一段区间内是否有被标记过的点。

代码:

#include <bits/stdc++.h>using namespace std;inline void read(int &x){    char ch;    bool flag=false;    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());    x=flag?-x:x;}inline void read(long long &x){    char ch;    bool flag=false;    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());    x=flag?-x:x;}inline void write(int x){    static const int maxlen=100;    static char s[maxlen];        if (x<0) {   putchar('-'); x=-x;}    if(!x){ putchar('0'); return; }    int len=0; for(;x;x/=10) s[len++]=x % 10+'0';    for(int i=len-1;i>=0;--i) putchar(s[i]);}int const MAXN=11000;int const MAXM=21000;int const MAXQ=61000;int n,m;int pre[ MAXM ] , now[ MAXN ] ,son[ MAXM ],tot;int pos[ MAXN ],cnt;int taken[ MAXN ];int siz[ MAXN ] , deep[ MAXN ];int top[ MAXN ] , fa[ MAXN ];void build(int a,int b){pre[++tot]=now[a];now[a]=tot;son[tot]=b;}void get_size_deep_fa(int x){siz[ x ] = 1;for (int p=now[x];p;p=pre[p])    if( son[p] != fa[ x ] )        {            deep[ son[p] ] = deep[ x ] + 1;            fa[ son[p] ] = x;            get_size_deep_fa( son[p]  );            siz[ x ] += siz[ son[p] ];        }}void get_pos_top(int x,int fa){bool op=0;int Max = 0;int Maxi = 0 ;++cnt;  pos[ x ] = cnt;for (int p = now[ x ] ; p ; p=pre[p] )    if ( son[p] != fa )        if ( Max < siz[ son[p] ] )            {                Max = siz[ son[p] ];                Maxi = son[p] ;            }if ( Maxi )    {        top[ Maxi ] = top[ x ];        get_pos_top( Maxi , x );    }for (int p = now[ x ]; p ; p=pre[p] )    if ( ( son[p]!=fa) && ( son[p]!=Maxi ) )        {            top[ son[p] ] = son[p];            get_pos_top( son[p] , x );        }}int lowbit( int x){return x&(-x);}int get_taken(int x ){int sum=0;while( x )    {        sum+=taken[ x ];        x-=lowbit( x );    }return sum;}void add(int x){while ( x<=n  )    {        taken[ x ]++;        x+=lowbit( x );    }}struct Query{    int u , v ,lca;    void get_lca(){    int x = u , y= v;    while ( top[ x ] != top[ y ] )        {            if ( deep [ top[ x ] ] > deep [ top[ y ] ])                swap ( x , y );            y = fa [ top[ y ] ];        }    lca = ( deep [ x ] < deep [ y ] )?x:y;    }    bool exist(){    int x = u , y= v;    while ( top[ x ] != top[ y ] )        {            if ( deep [ top[ x ] ] > deep [ top[ y ] ])                swap ( x , y );            if ( get_taken( pos[ y ] ) -  get_taken( pos[ top[ y ] ]-1 ) != 0 )                return 0;            y = fa [ top[ y ] ];        }    if ( get_taken( pos[ x ] ) - get_taken( pos[ lca ]-1 ) !=0)        return 0;    if ( get_taken( pos[ y ] ) - get_taken( pos[ lca ]-1 ) !=0)        return 0;    return 1;    }}que[ MAXQ ];bool cmp(Query A , Query B){return deep[ A.lca ] > deep[ B.lca ];}int main(){    while (scanf("%d",&n)!=EOF)        {            tot=0;            memset(now,0,sizeof(now));            n++;            for (int i=1;i<n;i++)                {                    int a,b;                    read(a); read(b);                    a++; b++;                    build( a , b );                    build( b , a );                }            cnt=0;            deep[1]=0;            fa[1]=0;            get_size_deep_fa( 1 );            top[1]=1;            get_pos_top( 1 , 0 );            read(m);            for (int i=1;i<=m;i++)                {                    read( que[i].u );                    read( que[i].v );                    que[i].u++; que[i].v++;                    que[i].get_lca();                }            sort( que+1 , que+m+1 , cmp );            memset( taken , 0 , sizeof( taken ) );            for (int i=1;i<=m;i++)                if (que[i].exist())                    add( pos [ que[i].lca ] );            printf("%d\n",get_taken( n ) );        }    return 0;}


阅读全文
0 0
原创粉丝点击