您的位置:首页 > 其它

【bzoj1040】[ZJOI2008]骑士 基环+外向树dp

2016-05-01 21:43 363 查看
这道题细节比较多。

我们可以把整个图分成若干个联通块,每个联通块都是一棵树+一条边。

对于每个联通块,我们找出环上的一条边(u,v),删去这条边,分别以u和v为根进行dp,最后把不取u和不取v中的最大值当做整个联通块的值。

最后的答案就是所有联通块的值的和。

注意处理重边的情况。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 1000010

using namespace std;

int head[maxn],to[2*maxn],next[2*maxn],dep[maxn];
long long w[maxn],f[2][maxn];
bool vis[2][maxn];
int root1,root2,n,num;
long long ans=0;

void addedge(int x,int y)
{
num++;to[num]=y;next[num]=head[x];head[x]=num;
}

void dfs(int x,int fa)
{
for (int p=head[x];p;p=next[p])
if (to[p]!=fa)
{
if (dep[to[p]]) {root1=x;root2=to[p];continue;}
dep[to[p]]=dep[x]+1;
dfs(to[p],x);
}
}

void dp1(int x,int fa)
{
f[1][x]=w[x];f[0][x]=0;vis[0][x]=1;
for (int p=head[x];p;p=next[p])
if (to[p]!=fa && !vis[0][to[p]])
{
if ((x==root2 && to[p]==root1)) continue;
dp1(to[p],x);
f[1][x]+=f[0][to[p]];
f[0][x]+=max(f[0][to[p]],f[1][to[p]]);
}
}

void dp2(int x,int fa)
{
f[1][x]=w[x];f[0][x]=0;vis[1][x]=1;
for (int p=head[x];p;p=next[p])
if (to[p]!=fa && !vis[1][to[p]])
{
if ((x==root1 && to[p]==root2)) continue;
dp2(to[p],x);
f[1][x]+=f[0][to[p]];
f[0][x]+=max(f[0][to[p]],f[1][to[p]]);
}
}

int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
long long x;
int y;
scanf("%lld%d",&x,&y);
w[i]=x;
addedge(i,y);addedge(y,i);
}
for (int i=1;i<=n;i++)
if (!vis[0][i])
{
dep[i]=1;dfs(i,0);
long long temp=0;
dp1(root1,0);temp=max(temp,f[0][root1]);
dp2(root2,0);temp=max(temp,f[0][root2]);
ans+=temp;
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: