思路:首先我们想到可以对相互憎恶的俩个骑士连边,这样就得到了一个图,有多个连通块,并且每个连通块中最多只有一个环。如果每个连通块都是一颗树,那么这个问题就很简单~每个节点都是选或者不选。
idea1:我想可不可以把这个比树多一条边的图,变成一棵树来处理,那么就是要删掉环上的一条边。考虑删掉这条边(u,v)的影响是什么,影响是u,v两点可能同时被选,产生错误的答案。
我的解决办法是把边删了的同时,分三种情况,1.把v[u]变成0 2.把v[v]变成0 3.把v[u],v[v]都变成0;因为如果一个点都变成0那答案中一定不包涵这个点。因为去掉这个点答案不会变劣;
产生的问题:
1.因为要删边,我用的是set<int> G[maxn]。这样复杂度就多了一个log。而且分三种情况,所以常数就比较大。T的我人都傻了
2.用set存会比vector消耗更多的内存。MLE。。。
3.cin即使加了流同步还是比scanf慢。。。所以还是用scanf吧
4.用数组实现邻接表会比用vector快一些
解决办法:
看了题解里的代码,发现这条边其实不用删,只要把u当成根,把v当成根分别算就行了,这样常数就小了。并且存图可以直接用邻接表,只要把其中一条边特判掉剩下的就是一棵树了
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000010
#define ll long long
#define pii pair<ll,ll>
ll v[maxn];int vis[maxn];
ll dp[maxn][2];
int sta,en,E;//环上的边
struct edge
{
int to;
ll v;
int nex;
}e[maxn*2];
int st[maxn],tot=0;
void add(int x,int y)
{
e[tot].to=y,e[tot].nex=st[x],st[x]=tot++;
}
void dfs1(int now,int fa)//fa是上一边
{
vis[now]=1;
for(int i=st[now];~i;i=e[i].nex)
{
if((i^1)==fa) continue;
if(vis[e[i].to])
{
sta=now,en=e[i].to;E=i;continue;
}
dfs1(e[i].to,i);
}
return ;
}
void dfs2(int now,int fa)
{
dp[now][0]=0;
dp[now][1]=v[now];
for(int i=st[now];~i;i=e[i].nex)
{
if((i^1)==fa) continue;
if(i==E||(i^1)==E) continue;
dfs2(e[i].to,i);
dp[now][0]+=max(dp[e[i].to][0],dp[e[i].to][1]);
dp[now][1]+=dp[e[i].to][0];
}
return ;
}
int main()
{
// ios::sync_with_stdio(false);
memset(st,-1,sizeof(st));
int n;cin>>n;
for(int i=0;i<n;i++)
{
int b;
// cin>>v[i+1]>>b;
scanf("%lld%d",&v[i+1],&b);
add(i+1,b);
add(b,i+1);
}
// for(int i=0;i<n*2;i++) cout<<e[i].first<<" "<<e[i].second<<endl;
ll ans=0;
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
dfs1(i,-1);//找环
// cout<<sta<<" "<<en<<endl;
dfs2(sta,-1);
ll t=0;
t=max(t,dp[sta][0]);
dfs2(en,-1);
t=max(t,dp[en][0]);
ans+=t;
}
// cout<<ans<<endl;
printf("%lld\n",ans);
return 0;
}