CF 372D Choosing Subtree is Fun

题目链接:http://codeforces.com/problemset/problem/372/D

题意:给出一棵树。找到一个树的子集。在该子集包含节点数不超过K的情况下,使得该子集包含的连续编号的节点数最大?

思路: 首先,DFS一次,得到一个DFS序,就是每个节点是第几个被DFS到的,这个程序中用cnt[u]表示,以及记录在DFS序中某几次遍历的编号是谁,这个用id[i]表示。id[cnt[u]]=u。然后二分答案。对于二分的值M,保证每次插入到set中的节点数都是连续的M个,比如第一次一次性插入M-1个点,然后每次插入一个,然后到下一个时删除已经插入到set中的最前面的那个。插入删除时更新子集中的点的个数。小于等于K则该二分值可达到。下面说在插入删除时如何更新答案。插入节点时插入的是每个节点的cnt值,即DFS序中的编号。每次插入时找到该编号的前一个编号的点以及后一个编号的点,不妨设为pre和next。此次插入的点需要新增的点数为dep[u]-dep[LCA(pre,u)]-dep[LCA(next,u)]+dep[LCA(pre,next)]。

 

vector<int> g[N];

int n,K;

int f[N][20],dep[N],id[N],cnt[N],tot;

set<int> S;





void DFS(int u,int pre)

{

    f[u][0]=pre;

    id[cnt[u]=++tot]=u;

    int i,v;

    FOR0(i,SZ(g[u]))

    {

        v=g[u][i];

        if(v!=pre) dep[v]=dep[u]+1,DFS(v,u);

    }

}



int LCA(int u,int v)

{

    if(dep[u]>dep[v]) swap(u,v);

    int k=dep[v]-dep[u],i;

    FOR0(i,20) if(k&(1<<i)) v=f[v][i];

    if(v==u) return u;

    for(i=19;i>=0;i--) if(f[u][i]&&f[v][i]&&f[u][i]!=f[v][i])

    {

        u=f[u][i];

        v=f[v][i];

    }

    return f[u][0];

}



int sum;





void modify(int op,int u)

{

    set<int>::iterator it=S.lower_bound(cnt[u]);

    if(*it==cnt[u])

    {

        S.erase(cnt[u]);

        it=S.lower_bound(cnt[u]);

    }

    if(!S.size())

    {

        if(op==1) S.insert(cnt[u]);

        return;

    }

    int pre,next;

    if(it==S.begin()) pre=id[*(--S.end())];

    else pre=id[*(--it)],it++;

    if(it==S.end()) next=id[*S.begin()];

    else next=id[*it];



    int temp=dep[u]-dep[LCA(pre,u)]-dep[LCA(next,u)]+dep[LCA(pre,next)];

    if(op==1) sum+=temp,S.insert(cnt[u]);

    else sum-=temp;

}



int OK(int M)

{

    sum=0;

    S.clear();

    int i;

    FOR1(i,M-1) modify(1,i);

    for(i=M;i<=n;i++)

    {

        modify(1,i);

        if(sum+1<=K) return 1;

        modify(0,i+1-M);

    }

    return 0;

}



int main()

{

    RD(n,K);

    int i,j;

    FOR1(i,n-1)

    {

        int u,v;

        RD(u,v);

        g[u].pb(v);

        g[v].pb(u);

    }

    DFS(1,0);

    for(i=1;i<20;i++) FOR1(j,n) f[j][i]=f[f[j][i-1]][i-1];

    int low=1,high=n,M,ans;

    while(low<=high)

    {

        M=(low+high)>>1;

        if(OK(M)) ans=M,low=M+1;

        else high=M-1;

    }

    PR(ans);

}

  

你可能感兴趣的:(tree)