BZOJ4472 JSOI2015salesman(树形dp)
相当于选一个包含根的连通块使权值和最大,且每个点的儿子选取数量有限制。那么显然贪心的在所有子树中选比较大的就可以了。至于方案是否唯一只需要看选的子树是否可以替换,注意dp值为0的情况。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 100010 int n,a[N],b[N],p[N],f[N],t,q[N]; bool flag[N]; struct data{int to,nxt; }edge[N<<1]; void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;} bool cmp(const int&a,const int&b) { return f[a]>f[b]; } void dfs(int k,int from) { f[k]=a[k]; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) dfs(edge[i].to,k); int cnt=0; for (int i=p[k];i;i=edge[i].nxt) if (edge[i].to!=from) q[++cnt]=edge[i].to; sort(q+1,q+cnt+1,cmp); for (int i=1;i<=min(cnt,b[k]);i++) if (f[q[i]]<0) break; else f[k]+=f[q[i]],flag[k]|=flag[q[i]]||f[q[i]]==0; if (cnt>b[k]&&f[q[b[k]]]>=0&&f[q[b[k]]]==f[q[b[k]+1]]) flag[k]=1; } int main() { #ifndef ONLINE_JUDGE freopen("bzoj4472.in","r",stdin); freopen("bzoj4472.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read(); for (int i=2;i<=n;i++) a[i]=read(); for (int i=2;i<=n;i++) b[i]=read()-1;b[1]=n; for (int i=1;i<n;i++) { int x=read(),y=read(); addedge(x,y),addedge(y,x); } dfs(1,1); cout<<max(f[1],0)<<endl; if (f[1]==0) flag[1]=1; cout<<(flag[1]?"solution is not unique":"solution is unique"); return 0; }