【HackerRank】【HourRank 20】Birjik and Nicole's Tree Game

题目大意

  给你一棵形态确定的 N 个点的有根树,1号节点为根,有 Q 个询问,每次询问如果把给定的 k 个点染色,子树中被染色节点数为 0,1,2,...,k 的节点分别有多少。
   N,Q3×105,k3×105


Solution

  果然是好久没打题了,看了题目又不会做。
  观察题目可以发现,由于 k3×105 ,所以应该把关注的重点放在每次染色的 k 个点上。注意到从下到上,每次子树中染色数发生变化时,都是当前点位于某些被染色节点的LCA处。所以我们只要把所有节点的LCA取出重新建树即可计算答案。
  这样的节点有多少个? k2 ?如果我们用dfs序表示出这颗树,我们可以发现当所有染色点按照dfs序的时间戳排序后,相邻两个的LCA全部拿出后可以发现正好就是所有可能的LCA。我们可以简单证明一下:
  考虑排序后被染色的点 ai aj ij
  1. |ij|=1 时,已经被计算过,加入了答案;
  2. |ij|>1 时,不妨设 i<j ,那么根据定义只会出现如下情况:
    a) ai 是LCA,此时所有节点 ak(i<k<j) 均在 ai 某个和 aj 相同的子树中,那么 ai ai+1 的LCA就是 ai
    b) ai aj 在它们的LCA aLCA 的两个不同子树中,那么当 ik<j 时,必然存在某个 k 使得 ak ak+1 aLCA 的两个不同子树中(否则它们均和 ai aLCA 的相同子树中),那么这时 ak ak+1 的LCA即为 aLCA .
  因此我们最多有 2k 个节点,通过它们按照原树的父子关系建立新树即可,点权为子树中被染色节点数,边权为两点原树间距离。
  这样我们就可以在 O((n+k)logn) 内完成。
  
  

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define rep(i,a,b) for (int i=a; i<=b; i++)
#define per(i,a,b) for (int i=a; i>=b; i--)
#define debug(x) {cout<<(#x)<<" "<
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1; char ch=getchar();
    while (!(ch>='0'&&ch<='9')) {if (ch=='-')f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') {x=x*10+(ch-'0'); ch=getchar();}
    return x*f;
}

const int N = 300005;

int n,q,k,ind=0;
vector<int> e[N],e2[N];
int dep[N],f[N][21];
int inp[N],oup[N];

inline void dfs(int x,int fa) {
    inp[x]=++ind; dep[x]=dep[fa]+1; f[x][0]=fa;
    rep(i,1,20) f[x][i]=f[f[x][i-1]][i-1];
    for (int i=0;i<(int)e[x].size();i++) if (e[x][i]!=fa) dfs(e[x][i],x);
    oup[x]=++ind;
}

inline int LCA(int u,int v) {
    if (dep[u]int d=dep[u]-dep[v];
    per(i,20,0) if (d&(1<20,0) if (f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i];
    if (u==v) return u;
    return f[u][0];
}

int cnt[N],num[N];

inline void dfs2(int x,int fa) {
    for (int i=0;i<(int)e2[x].size();i++) {
        dfs2(e2[x][i],x); num[x]+=num[e2[x][i]];
    }
    cnt[num[x]]+=dep[x]-dep[fa];
}

int ver[N<<1];
bool cmp(int a,int b) {
    return inp[a]int stk[N];

int main() {

    #ifndef ONLINE_JUDGE
    //  freopen("input43.txt","r",stdin);
    //  freopen("data.out","w",stdout);
    #endif

    n=read(); rep(i,1,n-1) {int u=read(),v=read(); e[u].push_back(v); e[v].push_back(u);} 
    dep[0]=0; dfs(1,0); 
    q=read();
    while (q--) {
        int k=read();
        int vsize=0;
        rep(i,1,k) {
            int x=read(); ver[++vsize]=x;
            num[x]=1;
        }
        sort(ver+1,ver+vsize+1,cmp);
        per(i,vsize,2) ver[++vsize]=LCA(ver[i],ver[i-1]);
        ver[++vsize]=1; sort(ver+1,ver+vsize+1,cmp);
        int nsize=1; rep(i,2,vsize) if (ver[i]!=ver[i-1]) ver[++nsize]=ver[i];
        vsize=nsize;
        int top=0; stk[++top]=ver[1];
        rep(i,2,vsize) {
            while (top>0&&oup[stk[top]]<=inp[ver[i]]) top--;
            stk[++top]=ver[i];
            e2[stk[top-1]].push_back(stk[top]);
        }
        rep(i,0,k) cnt[i]=0;
        dfs2(1,0);  cnt[0]=n; rep(i,1,k) cnt[0]-=cnt[i];
        rep(i,0,k) printf("%d ",cnt[i]); puts("");
        rep(i,1,vsize) {e2[ver[i]].clear(); num[ver[i]]=0;}
    }

    return 0;
}

你可能感兴趣的:(Hackerrank,最近公共祖先)