大意就是给你一颗树,每个点有一个权值w[i],求一个排列使得 所有的父亲都在儿子前面 并且排列的权值最小。
排列的权值在这里定义为 Σ i * w[p[i]] ,其中p[i] 是排列第i个位置的元素。
然后我瞎jb胡了一个算法,对于每个子树维护一个 p[],表示只考虑子树内的元素的最优排列。显然我们只要把一个点的所有儿子都合并之后再把这个点放在排列的第一个位置就可以了。(可以证明这些元素再往上走的时候相对位置还是一样的)
问题是怎么合并。
其实这就是个dp,合并x和y子树的时候,f[i][j] 表示 用了x子树前i个元素,用了y子树前j个元素,此时到终点的最优值是多少,然后顺带记录一下方案就好啦。
(昨天+今天调了 3h+ 主要是因为 fill 用了 0x7f 一直没有察觉233333,导致max一直是127(真是醉了))
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1005; int a[maxn][maxn],n,w[maxn],f[maxn][maxn]; int to[maxn*2],ne[maxn*2],hd[maxn],num,m; bool nxt[maxn][maxn]; inline void add(int x,int y){ to[++num]=y,ne[num]=hd[x],hd[x]=num;} inline void init(){ memset(hd,0,sizeof(hd)),num=0; memset(f,0x7f,sizeof(f)); memset(a,0,sizeof(a)); } inline void Merge(int x,int son){ int b[a[x][0]+a[son][0]+5],hzx[a[x][0]+5],hzs[a[son][0]+5]; hzx[a[x][0]+1]=0; for(int i=a[x][0];i;i--) hzx[i]=hzx[i+1]+a[x][i]; hzs[a[son][0]+1]=0; for(int i=a[son][0];i;i--) hzs[i]=hzs[i+1]+a[son][i]; f[a[x][0]][a[son][0]]=0; for(int i=a[x][0];i>=0;i--) for(int j=a[son][0];j>=0;j--){ if(i<a[x][0]&&f[i+1][j]<f[i][j]) f[i][j]=f[i+1][j],nxt[i][j]=1; if(j<a[son][0]&&f[i][j+1]<f[i][j]) f[i][j]=f[i][j+1],nxt[i][j]=0; f[i][j]+=hzx[i+1]+hzs[j+1]; } int now=0,nx=0,ny=0; while(++now<=a[x][0]+a[son][0]){ if(nxt[nx][ny]) nx++,b[now]=a[x][nx]; else ny++,b[now]=a[son][ny]; } for(int i=0;i<=a[x][0];i++) fill(f[i],f[i]+a[son][0]+1,2000000000); a[x][0]+=a[son][0]; for(int i=1;i<=a[x][0];i++) a[x][i]=b[i]; } void dfs(int x,int fa){ for(int i=hd[x];i;i=ne[i]) if(to[i]!=fa){ dfs(to[i],x); Merge(x,to[i]); } a[x][0]++; for(int i=a[x][0];i>1;i--) a[x][i]=a[x][i-1]; a[x][1]=w[x]; } inline void calc(int x){ int ans=0; for(int i=1;i<=n;i++) ans+=i*a[x][i]; printf("%d\n",ans); } int main(){ int uu,vv,root; while(scanf("%d%d",&n,&root)==2&&n&&root){ init(); for(int i=1;i<=n;i++) scanf("%d",w+i); for(int i=1;i<n;i++) scanf("%d%d",&uu,&vv),add(uu,vv),add(vv,uu); dfs(root,-1),calc(root); } return 0; }
原文地址:https://www.cnblogs.com/JYYHH/p/8975757.html
时间: 2024-11-01 14:01:47