测试地址:骑士
做法:被神题虐完之后用水题涨涨信心……
这一题需要用到环套树+树形DP。
初看这道题,点数和边数相同,很显然就是求一个环套树森林(注意不是一棵环套树!题目没有说明图是连通的!)的最大权值独立集。我们知道,如果是树的最大权值独立集,我们就可以使用树形DP来找答案,设
但是对于环套树,我们怎么计算它的最大权值独立集呢?我们一般是考虑将树形DP扩展到环套树的情况,环上的点进行特殊处理。首先对所有外向树进行树形DP,然后对于环上的某一个点
犯二的地方:求出环上的点后,老是忘记枚举的是环上的编号,而不是实际的编号,于是编号就乱了……以后要注意……
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
int n,tot=0,first[1000010]={0},lp[1000010],lplen;
ll val[1000010],f[1000010][2],flp0,flp1,ans=0;
bool inlp[1000010]={0},vis[1000010]={0},viss[1000010]={0},flag;
struct edge {int v,next,p;} e[2000010];
void insert(int a,int b,int p)
{
e[++tot].v=b,e[tot].p=p,e[tot].next=first[a],first[a]=tot;
}
void dfs(int v)
{
viss[v]=1;
for(int i=first[v];i;i=e[i].next)
if (!viss[e[i].v]) dfs(e[i].v);
}
bool find_loop(int v,int f)
{
vis[v]=1;
for(int i=first[v];i;i=e[i].next)
if (e[i].p!=f)
{
if (vis[e[i].v])
{
lp[1]=e[i].v;
inlp[e[i].v]=1;
lplen=1;flag=1;
return 1;
}
else if (find_loop(e[i].v,e[i].p))
{
if (flag)
{
lp[++lplen]=e[i].v;
inlp[e[i].v]=1;
}
if (v==lp[1]) flag=0;
return 1;
}
}
return 0;
}
void treedp(int v,int fa)
{
f[v][0]=0,f[v][1]=val[v];
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa&&!inlp[e[i].v])
{
treedp(e[i].v,v);
f[v][1]+=f[e[i].v][0];
f[v][0]+=max(f[e[i].v][0],f[e[i].v][1]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int j;
scanf("%lld%d",&val[i],&j);
insert(i,j,i),insert(j,i,i);
}
for(int i=1;i<=n;i++)
if (!viss[i])
{
flag=0;
dfs(i);
find_loop(i,0);
for(int j=1;j<=lplen;j++)
treedp(lp[j],0);
ll partans;
flp0=flp1=0;
for(int j=2;j<=lplen;j++)
{
ll tmp=flp0;
flp0=max(flp0,flp1)+f[lp[j]][0];
flp1=tmp+f[lp[j]][1];
}
partans=max(flp0,flp1)+f[lp[1]][0];
flp0=flp1=0;
for(int j=3;j<=lplen-1;j++)
{
ll tmp=flp0;
flp0=max(flp0,flp1)+f[lp[j]][0];
flp1=tmp+f[lp[j]][1];
}
partans=max(partans,max(flp0,flp1)+f[lp[1]][1]+f[lp[2]][0]+((lplen==2)?0:f[lp[lplen]][0]));
ans+=partans;
}
printf("%lld",ans);
return 0;
}