【[USACO12FEB]附近的牛Nearby Cows】
阿新 • • 發佈:2019-01-02
我記得我調這道題時中耳炎,發燒,於是在學長的指導下過了也沒有發題解
發現我自己的思路蠻鬼畜的
常規操作:\(f[i][j]\) 表示到\(i\)的距離為\(j\)的奶牛有多少隻,但注意這只是在第二遍dfs之後
在我的第一遍dfs中(就是下面那個叫build的函式),\(f[i][j]\)的含義是在i這課子樹中到\(i\)的距離為\(j\)的奶牛有多少隻,所以在第一遍dfs的時候,\(f[i][j]\)的狀態只會來自它的兒子們
於是在第一遍dfs就有一個異常簡單的方程
\[f[i][j]=\sum_{}f[k][j-1]\]
其中\(k\)是 \(i\)的兒子
如果我們欽定以1為根建樹的話,那麼1的子樹就是整棵樹,於是這個時候的\(f[1]\)
而這個時候第二遍dfs就要登場了,第二遍dfs的意義就是利用父親去更新兒子,於是我們就又有一個簡單的方程了
\[f[k][j]=\sum_{}f[i][j-1]\]
其中\(k\)是 \(i\)的兒子
這樣的話肯定會有重複的,因為到\(i\)的距離為2的點包含到\(k\)距離為1的k的兒子們,而這些點位於\(k\)的子樹中的點已經在第一遍dfs的時候被加上了,於是我們在這裡簡單容斥就好了
於是就是程式碼了
#include<iostream> #include<cstring> #include<cstdio> #define re register #define maxn 100001 using namespace std; struct node { int v,nxt; }e[maxn<<1]; int f[maxn][21],s[maxn],head[maxn],deep[maxn]; int n,num,k; inline int read() { char c=getchar(); int x=0; while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar(); return x; } inline void add(int x,int y) { e[++num].v=y; e[num].nxt=head[x]; head[x]=num; } inline void build(int r) { for(re int i=head[r];i;i=e[i].nxt) if(!deep[e[i].v]) { deep[e[i].v]=deep[r]+1; build(e[i].v); for(re int j=1;j<=k;j++) f[r][j]+=f[e[i].v][j-1]; } } inline void dfs(int r) { for(re int i=head[r];i;i=e[i].nxt) if(deep[e[i].v]>deep[r]) { for(re int j=k;j>=2;j--) f[e[i].v][j]-=f[e[i].v][j-2];//簡單的容斥原理了 //這裡的迴圈一定要倒序 for(re int j=1;j<=k;j++) f[e[i].v][j]+=f[r][j-1]; dfs(e[i].v); } } int main() { n=read(); k=read(); int x,y; for(re int i=1;i<n;i++) { x=read(); y=read(); add(x,y); add(y,x); } for(re int i=1;i<=n;i++) s[i]=read(),f[i][0]=s[i]; deep[1]=1; build(1); dfs(1); for(re int j=1;j<=n;j++) { int ans=0; for(re int i=0;i<=k;i++) ans+=f[j][i]; printf("%d\n",ans); } }