题目
解说
刚看完这道题感觉还是挺乱的,可能那时候脑子不太清醒,一度觉得自己又要重拾Tarjan了。当然最后还是发觉应该用树形DP。
(以下dp[u]代表以u为根的包括自己在内的子树的最大利润,bool g[u]表示u及其子树的方案数是否唯一,唯一则为0,否则为1,t[u]代表u的次数,v[u]代表u的价值)
计算最大利润确实挺简单。有点像之前做过的空调教室,但是多了次数限制和负数,但这不难处理。计算u的时候因为每个儿子在走完之后必须返回u来回到根节点,因此我们只能对儿子的dp值进行排序,选取前t[u]-1个,并且遇到负数立即停止因为接下来选的负数只能使总价值变小。
那么怎么处理方案是否唯一呢?
我们开一个bool数组g表示u及其子树的方案数是否唯一。显然,只有以下3种情况下方案数不唯一:
某个取得的儿子dp值为0(选不选都可以)
某个取得的儿子g值为1(在这颗子树中有不同的路径)
下个未选的儿子和最后选择的儿子f值相同(可以替换)(我写的时候忽略了这一点但是还是A了,大约是数据太弱了)
那么就从根开始递归一遍就可以了哦,最后看dp[1]和g[1]就可以了。
(部分实在想不上来的做法参考了DZN大佬的,谢谢啦~)
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=100000+5,Inf=2147483647; 4 inline int read(){ 5 int s=0,w=1; 6 char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar(); 9 return s*w; 10 } 11 int n,head[maxn],tot,v[maxn],t[maxn],dp[maxn]; 12 bool g[maxn]; 13 struct node{ 14 int to,next; 15 }e[2*maxn]; 16 void Add(int a,int b){ 17 e[tot].to=b; 18 e[tot].next=head[a]; 19 head[a]=tot; 20 tot++; 21 } 22 void dfs(int x,int fa){ 23 priority_queue<pair<int,int> > q;//按照dp值大小排序 24 for(int i=head[x];i;i=e[i].next){ 25 int v=e[i].to; 26 if(v==fa) continue; 27 dfs(v,x); 28 q.push(make_pair(dp[v],v)); 29 } 30 int num=0,sum=0,judge=0; 31 while(!q.empty()&&num<t[x]-1){ 32 if(q.top().first<0) break;//出现拖后腿的立即停止 33 if(q.top().first==0){//有0说明方案不为1, 34 //且0后面要么是0要么是负的,都无法做贡献,直接退 35 judge=1; 36 break; 37 } 38 sum+=q.top().first; 39 if(g[q.top().second]==1) judge=1; 40 q.pop(); 41 num++; 42 } 43 dp[x]=sum+v[x]; 44 g[x]=judge; 45 } 46 int main(){ 47 tot=1; 48 t[1]=Inf;//注意自己家可以走无数次 49 n=read(); 50 for(int i=2;i<=n;i++) v[i]=read(); 51 for(int i=2;i<=n;i++) t[i]=read(); 52 for(int i=1;i<=n-1;i++){ 53 int x,y; 54 x=read(); y=read(); 55 Add(x,y); 56 Add(y,x); 57 } 58 dfs(1,0); 59 printf("%d ",dp[1]); 60 if(g[1]) printf("solution is not unique"); 61 else printf("solution is unique"); 62 return 0; 63 }
幸甚至哉,歌以咏志。