CF.739B 【Alyona and a tree】(倍增+树上差分/二分树上差分)
题目链接:https://codeforces.com/contest/739/problem/B
题意:一棵根节点为1的树,每个点都有点值a[i],每条边也有权值,dist(v, u)表示从v到u边权和,当u时v的子孙并且dist(v, u)<=a[u]时u就受v控制,输出每个结点能控制的结点数。
解法1:倍增+树上差分
一开始不知道该怎么去写这个树上差分,后来看了别人的题解报告算是明白了。一开始就是简单的倍增,同时求出每个点x到1的距离,即d[x]=dis(1,x)。完成第一步dfs之后,就是把当前点x不断向上跳,比如跳到y了,dis(x,y)=d[x]-d[y]刚好<a[x],那么也就是说x~y里面所有的点(除去x)都可以控制x,那么就把y的父亲差分数组-1,x的父亲差分数组+1即可。
#include<bits/stdc++.h> #pragma GCC optimize(2) #define ll long long #define rep(i,a,n) for(int i=a;i<=n;i++) #define per(i,n,a) for(int i=n;i>=a;i--) #define endl '\n' #define eps 0.000000001 #define pb push_back #define mem(a,b) memset(a,b,sizeof(a)) #define IO ios::sync_with_stdio(false);cin.tie(0); using namespace std; const int INF=0x3f3f3f3f; const ll inf=0x3f3f3f3f3f3f3f3f; const int mod=1e9+7; const int maxn=2e5+5; int tot,head[maxn]; struct E{ int to,next,w; }edge[maxn<<1]; void add(int u,int v,int w){ edge[tot].to=v; edge[tot].w=w; edge[tot].next=head[u]; head[u]=tot++; } int n,a[maxn]; int fa[maxn][40],vis[maxn],depth[maxn],d[maxn]; void dfs(int x,int step){ depth[x]=step;vis[x]=1; for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; if(vis[v]) continue; fa[v][0]=x; d[v]=d[x]+edge[i].w; dfs(v,step+1); } } void bz(){ for(int j=1;j<=30;j++){ for(int i=1;i<=n;i++){ fa[i][j]=fa[fa[i][j-1]][j-1]; } } } int C[maxn]; void solve(int x,int f){ for(int i=head[x];i!=-1;i=edge[i].next){ int v=edge[i].to; if(v==f) continue; solve(v,x); C[x]+=C[v]; } } int main(){ scanf("%d",&n);mem(head,-1); rep(i,1,n) scanf("%d",&a[i]); rep(i,2,n){ int p,w;scanf("%d%d",&p,&w); add(i,p,w);add(p,i,w); } dfs(1,1); bz(); for(int i=1;i<=n;i++){ int now=i,dis=a[i]; for(int j=19;j>=0;j--){ if(d[now]-d[fa[now][j]]<=dis){ dis+=(d[fa[now][j]]-d[now]); now=fa[now][j]; } } C[fa[i][0]]++;C[fa[now][0]]--; } solve(1,0); rep(i,1,n){ printf("%d ",C[i]); } puts(""); }
前ICPC算法竞赛退役选手|现摸鱼ing