有一棵点数为 N 的树,树边有边权。给你一个在 0~ N 之内的正整
数 K ,你要在这棵树中选择 K个点,将其染成黑色,并将其他 的
N-K个点染成白色 。 将所有点染色后,你会获得黑点两两之间的距
离加上白点两两之间的距离的和的受益。问受益最大值是多少。
标签:
第一行包含两个整数 N, K 。
输出一个正整数,表示收益的最大值。
siz[x]=1; ren if(to[i]!=fa) { dp(to[i],x); rep(j,0,siz[x]) rep(k,0,siz[to[i]]) update(f[x][j],f[to[i]][k]); siz[x]+=siz[to[i]]; }
为什么呢?考虑那个二重循环,可以看做分别枚举两棵子树的每个点。你会发现,点对(u,v),只会在计算lca(u,v)的dp时才被考虑到,所以复杂度是O(n^2)。
#include<cstdio> #include<cctype> #include<queue> #include<cmath> #include<cstring> #include<algorithm> #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f; } typedef long long ll; const ll inf=1ll<<60; const int maxn=2010; int n,m,first[maxn],next[maxn<<1],to[maxn<<1],dis[maxn<<1],e; void AddEdge(int w,int v,int u) { to[++e]=v;dis[e]=w;next[e]=first[u];first[u]=e; to[++e]=u;dis[e]=w;next[e]=first[v];first[v]=e; } ll f[maxn][maxn],tmp[maxn],siz[maxn]; void dp(int x,int fa) { siz[x]=1; ren if(to[i]!=fa) { dp(to[i],x); rep(j,0,siz[x]) rep(k,0,siz[to[i]]) tmp[j+k]=max(tmp[j+k],f[x][j]+f[to[i]][k]+(ll)dis[i]*(k*(m-k)+(siz[to[i]]-k)*(n-m-siz[to[i]]+k))); siz[x]+=siz[to[i]]; rep(j,0,siz[x]) f[x][j]=tmp[j],tmp[j]=-inf; } } int main() { n=read();m=read(); rep(i,2,n) AddEdge(read(),read(),read()); rep(i,1,n) rep(j,2,m) f[i][j]=-inf; rep(i,0,n) tmp[i]=-inf; dp(1,0);printf("%lld\n",f[1][m]); return 0; }
标签:
原文地址:http://www.cnblogs.com/wzj-is-a-juruo/p/5013536.html