1. 程式人生 > >bzoj4719 [Noip2016]天天愛跑步(樹+lca+樹上差分+思路題)

bzoj4719 [Noip2016]天天愛跑步(樹+lca+樹上差分+思路題)

進入bzoj法眼的noip(lus)題hh。題解太麻煩啦。。。不寫啦。。。就體會一下思想:要是對於每條路徑操作,看他會影響哪些點的貢獻,鐵鐵的會t。所以考慮怎樣的一條路徑會對一個點產生貢獻,先討論簡化版的鏈的情況,發現要討論左右。結合另外兩個部分分,感覺就是把路徑分成兩半,x->lca,lca->y。分別討論,然後進行神奇的樹上差分,用vector來維護刪除操作。題解去觀摩這裡

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 300010
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; } int n,m,h[N],num=0,dep[N],fa[N][20],cnt[N],a[N<<1],ans[N]; int Log[N],w[N]; struct edge{ int
to,next; }data[N<<1]; struct Data{ int u,v,t,len; }path[N]; vector<int>ed[N]; vector<int>ed1[N]; vector<int>st[N]; void dfs(int x){ for(int i=1;i<=Log[n];++i){ if(!fa[x][i-1]) continue; fa[x][i]=fa[fa[x][i-1]][i-1]; }for(int i=h[x];i;i=data[i].next){ int
y=data[i].to;if(fa[x][0]==y) continue; fa[y][0]=x;dep[y]=dep[x]+1;dfs(y); } } inline int lca(int x,int y){ if(dep[x]<dep[y]) swap(x,y); int t=dep[x]-dep[y]; for(int i=Log[t];i>=0;--i) if(t&(1<<i)) x=fa[x][i]; if(x==y) return x; for(int i=Log[n];i>=0;--i) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i]; return fa[x][0]; } void calc(int x){ int res=a[w[x]+dep[x]];//記錄進入子樹前的值,避免把兄弟子樹統計進來 for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(y==fa[x][0]) continue;calc(y); }a[dep[x]]+=cnt[x]; ans[x]+=a[w[x]+dep[x]]-res; for(int i=0;i<ed[x].size();++i) a[ed[x][i]]--; } void calc1(int x){ int res=a[dep[x]-w[x]+300000]; for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(y==fa[x][0]) continue;calc1(y); } for(int i=0;i<st[x].size();++i) a[st[x][i]]++; ans[x]+=a[dep[x]-w[x]+300000]-res; for(int i=0;i<ed1[x].size();++i) a[ed1[x][i]]--; } int main(){ // freopen("running3.in","r",stdin); n=read();m=read();Log[0]=-1; for(int i=1;i<=n;++i) Log[i]=Log[i>>1]+1; for(int i=1;i<n;++i){ int x=read(),y=read(); 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);for(int i=1;i<=n;++i) w[i]=read(); for(int i=1;i<=m;++i){ int u=read(),v=read(),t=lca(u,v); path[i].u=u,path[i].v=v,path[i].t=t; path[i].len=dep[u]+dep[v]-2*dep[t]; cnt[u]++;ed[t].push_back(dep[u]); }calc(1); for(int i=1;i<=m;++i){ st[path[i].v].push_back(dep[path[i].v]-path[i].len+300000); ed1[path[i].t].push_back(dep[path[i].v]-path[i].len+300000); }calc1(1); for(int i=1;i<=m;++i) //對lca去重 if(dep[path[i].u]-dep[path[i].t]==w[path[i].t]) ans[path[i].t]--; printf("%d",ans[1]);for(int i=2;i<=n;++i) printf(" %d",ans[i]); return 0; }