P3177 [HAOI2015]樹上染色
阿新 • • 發佈:2018-08-22
直接 cst 顏色 esp spa n-1 註意 pre 距離 復制
數組在每一次用一棵新子樹跟新答案時記錄新答案,然後直接用其更新\(f\)數組的值即可
題目描述
有一棵點數為 N 的樹,樹邊有邊權。給你一個在 0~ N 之內的正整數 K ,你要在這棵樹中選擇 K個點,將其染成黑色,並將其他 的N-K個點染成白色 。 將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。
輸入輸出格式
輸入格式:
第一行包含兩個整數 N, K 。接下來 N-1 行每行三個正整數 fr, to, dis , 表示該樹中存在一條長度為 dis 的邊 (fr, to) 。輸入保證所有點之間是聯通的。
輸出格式:
輸出一個正整數,表示收益的最大值。
輸入輸出樣例
輸入樣例#1: 復制
3 1
1 2 1
1 3 2
輸出樣例#1:
3
說明
對於 100% 的數據, 0<=K<=N <=2000
求染色後黑點間距離之和加白點間距離之和之和的最大值,考慮去掉後效性的方法
對於每一棵子樹來說,其對於答案的貢獻其實只有子樹內同色點距離和,和黑點到子樹根的距離和\(*\)子樹外黑色點的個數+白色點到子樹根的距離和\(*\)子樹外白色點的個數,就可以轉移了,\(f[i][j]\) 表示\(i\)及其子樹內染黑\(j\)個點能都得到的最大收益。
由於子樹根節點的顏色對於其子樹的答案是沒有貢獻的(...) 所以把根節點染黑的情況就是\(f[i][j]=f[i][j-1]\)(...)
然後直接轉移就行了啊
需要註意的是因為一棵子樹都是必須要選的,是不能直接不選的,直接更新會對後面的更新在成影響,所以先建一個\(g\)
#include<iostream> #include<cstdio> #include<cstring> using namespace std; long long i,m,n,j,k,ver[10001],head[10001],nex[10001],edge[10001],z,cnt,x,y,f[2001][2001],g[2001],size[10001]; void add(long long x,long long y,long long z) { cnt+=1; ver[cnt]=y; nex[cnt]=head[x]; head[x]=cnt; edge[cnt]=z; } void dfs(long long now,long long fa) { f[now][0]=0; for(int i=head[now];i;i=nex[i]) { int t=ver[i]; if(t==fa) continue; dfs(t,now); memset(g,-0x3f,sizeof(g)); size[now]+=size[t]; for(long long j=min(size[now],m);j>=0;j--) for(int l=0;l<=min(size[t],j);l++) g[j]=max(g[j],f[now][j-l]+f[t][l]+edge[i]*l*(m-l)+edge[i]*(size[t]-l)*(n-m-size[t]+l)); for(int j=0;j<=min(size[now],m);j++) f[now][j]=g[j]; } size[now]+=1; if(size[now]==1) f[now][1]=0; f[now][size[now]]=f[now][size[now]-1]; for(int i=size[now];i>=1;i--) f[now][i]=max(f[now][i],f[now][i-1]); } int main() { scanf("%lld%lld",&n,&m); memset(f,-0x3f,sizeof(f)); for(i=1;i<n;i++) { scanf("%lld%lld%lld",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(1,0); printf("%lld",f[1][m]); }
(話說這道題作為一道連數據範圍都沒有的題,會爆int是不是有點過分啊啊啊啊啊 (ノ>д<)ノ彡┻━┻
P3177 [HAOI2015]樹上染色