洛谷P2607 騎士 樹形dp

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

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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章