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

来源:互联网 发布:淘宝电商需要什么条件 编辑:程序博客网 时间:2024/06/01 10:15

题目大意

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


Solution

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

#include<set>#include<map>#include<queue>#include<cmath>#include<string>#include<cstdio>#include<vector>#include<cassert>#include<cstring>#include<iostream>#include<algorithm>#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)<<" "<<x<<endl;}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]<dep[v]) swap(u,v);    int d=dep[u]-dep[v];    per(i,20,0) if (d&(1<<i)) u=f[u][i];    per(i,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]<inp[b];}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;}
0 0
原创粉丝点击