题目链接
https://www.luogu.com.cn/problem/P6082
题意
一棵树,每个点有收益(可负),经过一点获得其收益(多次经过不重复,一定获得不可放弃),每个点有最大停留次数(最小为2),起点为1,起点收益0,停留无限。问如何游览会最大化收益(可以在起点摸鱼不走)
思路
树形DP
dp[i]代表i为根子树最大收益,首先明确一个点的限制li[i],让他只能前往li[i]-1个子树。而一个点的收益就应当是所有子树中满足限制的最大的正DP值之和。
d
p
[
i
]
=
∑
1
l
i
[
i
]
−
1
d
p
[
j
]
(
j
为
i
子
节
点
且
d
p
[
j
]
>
0
)
dp[i]=\sum_{1}^{li[i]-1} dp[j] \ \ \ \ (j为i子节点且 dp[j]>0)
dp[i]=1∑li[i]−1dp[j] (j为i子节点且dp[j]>0)
其中dp[j]从大到小排序
再考虑多种情况,稍微推一下就知道,假设限制是走t个子树,那么排序后的子树中,如果第t个和第t+1个数相等,那么一定是出现了不唯一(5,3,3,2,2 选3个是唯一的,选四个就不唯一了)。此外,如果出现了dp值为0,且可以被选择(排序后在li[i]-1范围内),那么他是可选可不选的一条路线那么一定不唯一。
以上
教训/收获
细节细节细节!!!
代码
#include<cstdio>
#include<iostream>
#include<iomanip>
#include<map>
#include<unordered_map>
#include<string>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<chrono>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl "\n"
//#define int long long
//#define double long double
using namespace std;
typedef long long ll;
const int maxn=100505;
const int inf=0x3f3f3f3f;
int n,m,k;
int va[maxn],li[maxn];
vector<int>e[maxn];
int dp[maxn];
int ans;
bool uni=1;
bool cmp(int a,int b){
return dp[a]>dp[b];
}
void dfs(int x,int fa){
for(auto y:e[x]){
if(y==fa) continue;
dfs(y,x);
}
vector<int>v;
for(auto y:e[x]){
if(y==fa) continue;
if(dp[y]>=0)
v.push_back(y);
}
sort(v.begin(),v.end(),cmp);
int t=li[x];
t--;
if(t<v.size()&&v[t]==v[t-1]) uni=0;
for(auto i :v){
t--;
dp[x]+=dp[i];
if(dp[i]==0) uni=0;
if(!t) break;
}
dp[x]+=va[x];
}
signed main(){
IOS
#ifndef ONLINE_JUDGE
freopen("IO\\in.txt","r",stdin);
freopen("IO\\out.txt","w",stdout);
#endif
int tn=1;
cin>>n;
for(int i=2;i<=n;i++) cin>>va[i];
for(int i=2;i<=n;i++) cin>>li[i];
va[1]=0;
li[1]=inf;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,-1);
//for(int i=1;i<=n;i++) cout<<i<<' '<<dp[i]<<endl;
cout<<dp[1]<<endl;
if(uni) cout<<"solution is unique"<<endl;
else cout<<"solution is not unique"<<endl;
}