bzoj5186 [Usaco2018 Jan]Cow at Large(树+dfs序+分块+BIT/点分治)

首先处理出数组mn[x],表示x到离他最近的叶子的距离。如果给定起始位置u,我们以u为根,处理出dis[x]表示x到u的距离,则节点x对答案有贡献当且仅当mn[x]<=dis[x]&&mn[fa[x]]> dis[fa[x]]。我们发现所有满足mn[x]<=dis[x]的点x构成的集合S就是由若干颗子树构成的。(除非起点u为叶子,则集合S就是这棵树本身,只含有一棵树,要特判掉)。

且答案就是子树个数。如何计算子树个数呢?我们处理出每个点的度数deg,我们发现一棵子树的所有点的 v2deg[v]=1 ∑ v 2 − d e g [ v ] = 1 (因为一棵k个点的子树的度数和为2k-1)。于是我们可以通过计算出所有的点v的 v2deg[v] ∑ v 2 − d e g [ v ] 得到子树个数。

于是我们就是要从u为根转移到v为根时,维护这个集合S,以及这个和。我们发现这一转移的影响就是:对v的子树的dis都-1,其他点的dis都+1。于是我们可以先做出dfs序,把子树对应到区间,然后区间加法。可以对dfs序分块,每一块维护一个BIT来实现。时间复杂度 O(nnlogn) O ( n n l o g n ) 。空间复杂度 O(2nn) O ( 2 n n ) ,bzoj上空间开不下,时间跑不出。。。

还有一种比较优越的点分治做法,可以做到 O(nlogn) O ( n l o g n ) ,待更新。

upd:orz Pickupwin sro orz Pickupwin sro
sbw巨佬太神了。我们先默认给每个答案加上所有点的贡献,其实就是2。然后我们来减去那些不合法的点的贡献。
我们点分治。每次处理出到重心的距离dis。则dis[x]+dis[B]< mn[x]时,x对B的贡献是不合法的。即dis[B]< mn[x]-dis[x].
于是我们可以先dfs一遍把所有的mn[x]-dis[x]的贡献统计进一个数组。然后再dfs一遍枚举B,减去所有不合法的贡献,即>dis[B]的数组的后缀和。注意容斥掉x,B在同一棵子树的情况。

dfs序分块+BIT

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 70010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,h[N],num=0,mn[N],dis[N],fa[N],du[N],in[N],out[N],dfn=0;
int nn,bel[N],lazy[270],alllazy=0,c[270][100010],v[N],w[N],ans[N];
struct edge{
    int to,next;
}data[N<<1];
inline void add(int id,int x,int val){for(;x<=n*2+1;x+=x&-x) c[id][x]+=val;}
inline int ask(int id,int x){int res=0;for(;x;x-=x&-x) res+=c[id][x];return res;}
void dfs(int x){
    mn[x]=inf;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]) continue;
        fa[y]=x;dis[y]=dis[x]+1;dfs(y);mn[x]=min(mn[x],mn[y]+1);
    }if(du[x]==1) mn[x]=0;
}
inline void dfs3(int x){
    in[x]=++dfn;w[in[x]]=2-du[x];
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]) continue;
        mn[y]=min(mn[y],mn[x]+1);dfs3(y); 
    }out[x]=dfn;v[in[x]]=mn[x]-dis[x]+n+1;
    add(bel[in[x]],v[in[x]],w[in[x]]);
}
inline int getsum(int x){
    if(du[x]==1) return 1;
    int res=0;
    for(int i=1;i<=bel[n];++i) res+=ask(i,n+1-lazy[i]-alllazy);return res;
}
inline void rebuild(int id,int x,int y,int val){
    int l=(id-1)*nn+1,r=min(id*nn,n);
    for(int i=l;i<=r;++i) add(id,v[i],-w[i]);
    for(int i=l;i<=r;++i) v[i]+=lazy[id];
    for(int i=x;i<=y;++i) v[i]+=val;lazy[id]=0;     
    for(int i=l;i<=r;++i) add(id,v[i],w[i]);
}
inline void change(int x,int y,int val){
    if(bel[x]==bel[y]){rebuild(bel[x],x,y,val);return;}
    for(int i=bel[x]+1;i<bel[y];++i) lazy[i]+=val;
    rebuild(bel[x],x,bel[x]*nn,val);rebuild(bel[y],(bel[y]-1)*nn+1,y,val);
}
void dfs2(int x){
    ans[x]=getsum(x);
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==fa[x]) continue;
        change(in[y],out[y],2);alllazy--;dfs2(y);
        change(in[y],out[y],-2);alllazy++;
    }
}
int main(){
//  freopen("atlarge.in","r",stdin);
//  freopen("atlarge.out","w",stdout);
    n=read();nn=sqrt(n);
    for(int i=1;i<=n;++i) bel[i]=(i-1)/nn+1;
    for(int i=1;i<n;++i){
        int x=read(),y=read();du[x]++;du[y]++;
        data[++num].to=y;data[num].next=h[x];h[x]=num;
        data[++num].to=x;data[num].next=h[y];h[y]=num;
    }dfs(1);dfs3(1);dfs2(1);
    for(int i=1;i<=n;++i) printf("%d\n",ans[i]);
    return 0;
}

点分治

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 70010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,h[N],num=0,ans[N],du[N],mn[N],dis[N],sz[N],f[N],a[N],b[N],mx,tot;
bool vis[N];int mx1=0,sumsz,rt;
struct edge{
    int to,next;
}data[N<<1];
void dfs4(int x,int Fa){
    mn[x]=inf;if(du[x]==1) mn[x]=0;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa) continue;
        dfs4(y,x);mn[x]=min(mn[x],mn[y]+1);
    }
}
void dfs5(int x,int Fa){
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa) continue;
        mn[y]=min(mn[y],mn[x]+1);dfs5(y,x);
    }
}
void dfs1(int x,int Fa){
    sz[x]=1;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dfs1(y,x);sz[x]+=sz[y];
    }
}
void dfs2(int x,int Fa){
    f[x]=0;
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dfs2(y,x);f[x]=max(f[x],sz[y]);
    }f[x]=max(f[x],sumsz-sz[x]);if(f[x]<f[rt]) rt=x;
}
void dfs3(int x,int Fa){
    int d=mn[x]-dis[x];
    if(d>0) mx=max(mx,d),a[d]+=2-du[x];
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dis[y]=dis[x]+1;dfs3(y,x);
    }
}
void dfs6(int x,int Fa){
    int d=mn[x]-dis[x];
    if(d>0) mx1=max(mx1,d),b[d]+=2-du[x];
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;dfs6(y,x);
    }
}
void dfs7(int x,int Fa){
    ans[x]+=a[dis[x]+1]-b[dis[x]+1];
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa||vis[y]) continue;
        dfs7(y,x);
    }
}
inline void gao(int x){
    mx=0;tot=0;dis[x]=0;dfs3(x,0);
    for(int i=mx-1;i>=1;--i) a[i]+=a[i+1];
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(vis[y]) continue;
        mx1=0;dfs6(y,x);for(int i=mx1-1;i>=1;--i) b[i]+=b[i+1];
        dfs7(y,x);for(int i=mx1;i>=1;--i) b[i]=0;
    }ans[x]+=a[1];for(int i=mx;i>=1;--i) a[i]=0;
}
inline void solve(int x){
    vis[x]=1;dfs1(x,0);gao(x);
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(vis[y]) continue;
        sumsz=sz[y];rt=0;dfs2(y,x);solve(rt);
    }
}
int main(){
    n=read();f[0]=inf;
    for(int i=1;i<n;++i){
        int x=read(),y=read();du[x]++;du[y]++;
        data[++num].to=y;data[num].next=h[x];h[x]=num;
        data[++num].to=x;data[num].next=h[y];h[y]=num;
    }dfs4(1,0);dfs5(1,0);dfs1(1,0);sumsz=n;rt=0;dfs2(1,0);
    solve(rt);
    for(int i=1;i<=n;++i){
        if(du[i]==1){puts("1");continue;}
        printf("%d\n",2-ans[i]);
    }return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值