洛谷P2607 [ZJOI2008]骑士(基环树)

传送门

首先这是一个有$n$个点$n$条边的图(据大佬们说这玩意儿叫做基环树?)

不难(完全没有)发现每个连通块里最多只有一个环

那么找到这个环,然后把它断开,再对它的两个端点分别跑树形dp

设$dp[u][0]$表示该点不选,$dp[u][1]$表示选,然后跑一个没有上司的舞会就可以了

 //minamoto
#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[<<],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,:;}
inline int read(){
#define num ch-'0'
char ch;bool flag=;int res;
while(!isdigit(ch=getc()))
(ch=='-')&&(flag=true);
for(res=num;isdigit(ch=getc());res=res*+num);
(flag)&&(res=-res);
#undef num
return res;
}
const int N=1e6+;
int head[N],Next[N<<],ver[N<<],tot=;
inline void add(int u,int v){
ver[++tot]=v,Next[tot]=head[u],head[u]=tot;
}
int vis[N],val[N],n,x1,x2,E;ll dp[N][],ans=;
void find(int u,int l){
vis[u]=;
for(int i=head[u];i;i=Next[i]){
if((i^)==l) continue;
if(vis[ver[i]]){
x1=u,x2=ver[i],E=i;continue;
}find(ver[i],i);
}
}
void dfs(int u,int l){
dp[u][]=,dp[u][]=val[u];
for(int i=head[u];i;i=Next[i]){
if((i^)==l) continue;
if(i==E||(i^)==E) continue;
dfs(ver[i],i);
dp[u][]+=dp[ver[i]][],dp[u][]+=max(dp[ver[i]][],dp[ver[i]][]);
}
}
int main(){
// freopen("testdata.in","r",stdin);
n=read();
for(int i=,v;i<=n;++i)
val[i]=read(),v=read(),add(i,v),add(v,i);
for(int i=;i<=n;++i){
if(vis[i]) continue;
find(i,),dfs(x1,);
ll res=dp[x1][];
dfs(x2,);
cmax(res,dp[x2][]);
ans+=res;
}
printf("%lld\n",ans);
return ;
}
上一篇:CSS自学笔记(5):CSS的样式


下一篇:CImageList用法介绍