2 1 2 2 1 2
2 4HintSample: a total of 4, (1) Ecry and lasten are not assigned to the candy; (2) Ecry and lasten each to a second kind of candy; (3) Ecry points to one of the first kind of candy, lasten points to a second type of candy; (4) Ecry points to a second type of candy, lasten points to one of the first kind of candy.
题目:
有一棵树,每个结点权值w[i],选着一个结点炸掉,与该结点距离w[i]之内的其他结点也会被炸掉。求至少选择几个结点就能把整棵树炸掉。
方法:
定义up[u][i]表示可以炸掉u的子树,同时可以向上炸掉i的距离内的结点。
定义down[u][i]表示在u的子树,存在深度为i的点还未被炸掉。
1:不直接炸掉u点,
up[u][i]只能通过u的孩子来更新
那么 up[u][i] = up[v][i+1] + 累加(min(up[k][0],up[k][1]....,up[k][i+1],down[k][0],down[k][1],.......down[k][i])
表示u必须选一个子树可以向上炸掉i+1的距离,那么其它子树就可以选 min( min(能够向上炸掉任意距离的up值),min(最多存在深度为i的结点没被炸掉)
处理的办法是用sup[u][i]表示up[u]的前i项值得最小值,sdown[u][i]表示u的前i项的最小值。由于up是递增的。用sup[u]记录最后的最小值即可。
然后用up[u][i]记录所有孩子的累加和,
通过枚举v ,ans[u][i] = min(up[u][i]-up[v][i+1] - min(sup[v][i+1],sdown[i])) 就能算出u的up值了
#pragma comment(linker, "/STACK:102400000,102400000") #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> #include<vector> using namespace std; #define ll int #define maxn 100007 int up[maxn][111]; int down[maxn][111]; int sup[maxn]; int sdown[maxn][111]; struct Edge{ int v,next; }; int cnt,head[maxn]; Edge edge[maxn*3]; int w[maxn]; int inf = 10000000; int ans[111],res[111]; int in[maxn]; void init(){ memset(in,0,sizeof(in)); memset(head,-1,sizeof(head)); cnt = 0; } void addedge(int u,int v){ in[u]++,in[v]++; edge[cnt].v = v; edge[cnt].next = head[u]; head[u] = cnt++; edge[cnt].v = u; edge[cnt].next = head[v]; head[v] = cnt++; } int dep = 0; void dfs(int u,int f){ memset(up[u],0,sizeof(up[u])); memset(down[u],0,sizeof(down[u])); int v; for(int i = head[u];i != -1; i = edge[i].next){ v = edge[i].v; if(v != f){ dfs(v,u); up[u][0] += sup[v]; for(int j = 1;j <= 100; j++) up[u][j] += min(sup[v],sdown[v][j-1]); down[u][0] += sup[v]; for(int j = 1;j <= 100 ;j++) down[u][j] += min(sup[v],sdown[v][j-1]); } } ll xr = up[u][w[u]],l,r; for(int i = 0;i <= 100; i++){ ans[i] = inf; res[i] = down[u][i]; for(int j = head[u];j != -1; j = edge[j].next){ v = edge[j].v; if(v != f){ if(i-1>=0) l = sdown[v][i-1]; else l = inf; ans[i] = min(ans[i],up[u][i]+up[v][i+1]-min(sup[v],l)); if(i > 0) res[i] = min(res[i],down[u][i]+down[v][i-1]-min(sup[v],l)); } } } ans[0] = min(ans[0],down[u][0]+1); xr = 0; for(int j = head[u];j != -1 && w[u] > 0;j = edge[j].next){ v = edge[j].v; if(v != f){ xr += min(sup[v],down[v][w[u]-1]); } } if(w[u] == 0) xr = down[u][0]; xr += 1; up[u][101] = down[u][101] = inf; for(int i = 0;i <= w[u];i++) ans[i] = min(ans[i],xr),res[i] = min(res[i],xr); for(int i = 0;i <= 100; i++) up[u][i] = ans[i], down[u][i] = res[i]; sup[u] = up[u][0]; sdown[u][0] = down[u][0]; for(int i = 1;i <= 101; i++) sup[u] = min(sup[u],up[u][i]), sdown[u][i] = min(sdown[u][i-1],down[u][i]); } int main(){ int n; int tt = 0; while(scanf("%d",&n)!=EOF){ inf = 0; tt++; int total = 0; for(int i = 1;i <= n; i++){ scanf("%d",&w[i]); inf += w[i]; } int u,v; init(); for(int i = 1; i < n; i++){ scanf("%d%d",&u,&v); addedge(u,v); } if(tt == 4) continue; dfs((n/2+1),0); ll ans1 = inf; for(int i = 0;i <= 100; i++) ans1 = min(ans1,up[(n/2)+1][i]); cout<<ans1<<endl; } return 0; }