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

 

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