思路:首先我們想到可以對相互憎惡的倆個騎士連邊,這樣就得到了一個圖,有多個連通塊,並且每個連通塊中最多隻有一個環。如果每個連通塊都是一顆樹,那麼這個問題就很簡單~每個節點都是選或者不選。
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;
}