骑士-没有上司的舞会增强版(树上DP+基环树)
环基树就是有环的树
骑士 A 憎恨 骑士 B,则 B 出现 A 就不会出现
而一个骑士可以被多个骑士憎恨,一个骑士最多憎恨一个人,那么把关系改为谁被谁憎恨就是经典树状图了
而每个骑士都有憎恨的人,所以有 N 条边,必定会形成环,也不一定只有一块连通图
那如何处理?
因为每个人只能憎恨一个人,即被某个人憎恨的只有一个,说明每个点的入度只能为1
对于一颗 正常的树 ,只有顶点入度为 0 ,其他点的入度已经为 1 了
说明要形成环基树 ,之能将两颗正常树的顶点互连才行
所以,任选一点不断 getfather 就一定能到达环
我们选择环中任意一对相邻的点 A B ,A 出现则 B 不出现 ,B 出现则 A 不出现 ,或者 A 、B 都不出现
总之就是不能两者同时出现
那么就要 max 分别dfs+dp两点不出现,这样能保证只会出现上述三种情况
CODE
#include <bits/stdc++.h>
using namespace std;
typedef long long int LL;
const int N=1e6+10;
LL n,ans,p[N],f[N],dp[N][2],mark;
vector<LL> edge[N];
bool vis[N];
void add(LL fa,LL to)
{
edge[fa].push_back(to);
f[to]=fa;
}
void dfs(LL now)
{
vis[now]=true;
dp[now][1]=p[now];
dp[now][0]=0;//每次都要重置为0,因为是分别计算DP
for(LL to:edge[now])
{
if(to==mark) continue;//跳过当前起始点避免循环
dfs(to);
dp[now][0]+=max(dp[to][0],dp[to][1]);
dp[now][1]+=dp[to][0];
}
}
LL sovle(LL now)
{
vis[now]=true;//标记该树每个点,方便以后找到每个独立的连通图
mark=now;
LL res=0;
while(!vis[f[mark]])//溯源找环
{
vis[f[mark]]=true;
mark=f[mark];
}
dfs(mark);
res=dp[mark][0];
mark=f[mark];
dfs(mark);
res=max(res,dp[mark][0]);
return res;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(LL i=1;i<=n;i++)
{
f[i]=i;
}
for(LL i=1;i<=n;i++)
{
LL from;//被憎恨的骑士就是“上司”
cin>>p[i]>>from;
add(from,i);
}
for(LL i=1;i<=n;i++)
{
if(vis[i]) continue;
ans+=sovle(i);//累加每个连通图的最大战力
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号