[ZJOI2008] 骑士
基环树板子。突然想起了一年以前的那个傍晚和黄忠公园的蚊子。往事不堪回首。
基环树找环采用了题解区的一种比较好理解的方法,即拓扑排序。无向图中不断去除度为1的结点,剩下的一定都是环上的结点(虽然可能是基环树森林)。然后可以枚举环上的结点,每次solve都先找环,然后求出这个点对应的树的答案。有一些特殊情况,比如只有两个点的环。代码如下:
int now=wh,cnt=0;
do{
c[++cnt]=now;dfs(now,0);
for(int i=head[now],th;i;i=e[i].next){
if(tr[th=e[i].t]||th==c[cnt-1])continue;
now=th;break;
}
}while(now!=c[cnt]&&now!=wh);
dp部分就比较简单了。注意断环成链时链首和链尾不能同时选,需要强制一个不选之后再进行环上dp。有一些细节要注意,但总体而言还是比较好写的。
#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=1000010;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline void check(int &s1,int s2){
s1=s1<s2?s2:s1;return;
}
inline int max(int s1,int s2){
return s1<s2?s2:s1;
}
int m,a[N],ans;
struct edge{
int t,next;
}e[N<<1];
int head[N],esum;
inline void add(int fr,int to){
e[++esum]=(edge){to,head[fr]};head[fr]=esum;
}
int d[N];bool tr[N];
queue<int>q;
void find_circle(){
for(int i=1;i<=m;i++){
if(d[i]==1)q.push(i);
}
while(!q.empty()){
int wh=q.front();q.pop();tr[wh]=true;//printf("wh=%lld\n",wh);
for(int i=head[wh],th;i;i=e[i].next){
d[th=e[i].t]--;
if(d[th]==1)q.push(th);
}
}
return;
}
int c[N],cnt;
int f[N],g[N],r[N][2];//选不选自己
inline void dfs(int wh,int fa){
for(int i=head[wh],th;i;i=e[i].next){
if(tr[th=e[i].t]==false||th==fa)continue;
dfs(th,wh);
g[wh]+=max(f[th],g[th]);
f[wh]+=g[th];
}
f[wh]+=a[wh];
}
inline void solve(int wh){
int now=wh,an=0;cnt=0;
do{
c[++cnt]=now;dfs(now,0);
for(int i=head[now],th;i;i=e[i].next){
if(tr[th=e[i].t]||th==c[cnt-1])continue;
now=th;break;
}
}while(now!=c[cnt]&&now!=wh);
r[c[1]][0]=g[c[1]],r[c[1]][1]=f[c[1]];now=c[1];
for(int i=2;i<cnt;i++){
now=c[i];int last=c[i-1];
r[now][1]=f[now]+r[last][0];
r[now][0]=g[now]+max(r[last][0],r[last][1]);
}
check(an,max(r[now][0],r[now][1])+g[c[cnt]]);
r[c[cnt]][0]=g[c[cnt]],r[c[cnt]][1]=f[c[cnt]];now=c[cnt];
for(int i=cnt-1;i>=2;i--){
now=c[i];int last=c[i+1];
r[now][1]=f[now]+r[last][0];
r[now][0]=g[now]+max(r[last][0],r[last][1]);
}
check(an,max(r[now][0],r[now][1])+g[c[1]]);
//for(int i=1;i<=cnt;i++)printf("%lld ",c[i]);printf("\n");
ans+=an;
for(int i=1;i<=cnt;i++)tr[c[i]]=true;
}
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);int s1;
for(int i=1;i<=m;i++){
read(a[i]);read(s1);
add(i,s1);add(s1,i);
d[i]++;d[s1]++;
}
find_circle();
for(int i=1;i<=m;i++){
if(tr[i])continue;
solve(i);
}
printf("%lld\n",ans);
return 0;
}
一如既往,万事胜意