[EC Final 2022] Coloring 题解
连边
先考虑如果
于是做树形 DP,设
可以针对这个 DP 的过程进行操作的构造,所以它的正确性并没有问题。使用前缀
于是对于
对于
对于
前者是显然的,因为一个结点的前驱没法给它提供更多的次数。后者只需考虑最大化不等式左边的值,让
于是枚举
时间复杂度
放代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll I=1e18;
inline void chmax(ll &x,ll y){if(y>x)x=y;}
vector<int> find_cycle(vector<int> a){
int x=0; vector<bool> b(a.size());
while(!b[x])b[x]=true,x=a[x];
fill(b.begin(),b.end(),false);
vector<int> c;
while(!b[x])c.emplace_back(x),b[x]=true,x=a[x];
return c;
} // 基环树找环
int main(){
ios::sync_with_stdio(false);
int n,s; cin>>n>>s,s--;
vector<int> w(n),p(n),a(n);
for(auto &i:w)cin>>i;
for(auto &i:p)cin>>i;
for(auto &i:a)cin>>i,i--;
vector<vector<int> > g(n);
for(int i=0;i<n;i++)
g[a[i]].emplace_back(i);
vector<int> c=find_cycle(a);
reverse(c.begin(),c.end());
vector<bool> b(n);
for(int i:c)b[i]=true;
vector f(n,vector<ll>(n+2));
function<void(int)> dfs=[&](int u){
for(int i=0;i<=n;i++)
f[u][i]=(i&1^(u==s)?w[u]:0)-(ll)i*p[u];
f[u][n+1]=-I;
for(int i:g[u])
if(!b[i]){
dfs(i); ll mx=-I;
for(int j=0;j<=n;j++){
chmax(mx,f[i][j]);
f[u][j]+=max(mx,u==s?f[i][j+1]:-I);
} // 前缀和优化转移
}
}; // 进行树形 DP
if(b[s]){
rotate(c.begin(),find(c.begin(),c.end(),s),c.end());
// 使 s 成为环上第一个点
for(int i:c)dfs(i); // 对于环上每个点跑树形 DP
if(c.size()==2)cout<<max({f[s][0]+f[c[1]][0],f[s][0]+f[c[1]][1],f[s][1]+f[c[1]][0]})<<endl,exit(0);
// 注意特判 k=2
ll w=-I;
for(int r=1;r<=n+1;r++){
vector g(c.size(),vector<ll>(3,-I));
g[0][0]=f[s][r-1];
for(int i=1;i<c.size();i++){
chmax(g[i][0],g[i-1][0]+f[c[i]][r]);
chmax(g[i][1],max(g[i-1][0],g[i-1][1])+f[c[i]][r-1]);
if(r>1)chmax(g[i][2],max({g[i-1][0],g[i-1][1],g[i-1][2]})+f[c[i]][r-2]);
} // 在环上跑外层 DP
chmax(w,*max_element(g.back().begin(),g.back().end()));
}
cout<<w<<endl;
}
else dfs(s),cout<<max(f[s][0],f[s][1])<<endl;
return 0;
}