前言
环套树者,一个环套一棵树也
解析
定义:n个点,n条边的无向连通图
其实就是树多了一条边,连出了一个环
性质:如果对环套树进行dfs,多出的一条非树边一定是一条返祖边
考虑dfs的过程如果它不是返祖边,dfs的时候就会直接从这条边过去,该边就会成为树边了
找环
如何在环套树上找到非树边?
考虑dfs,如果出边指向已经被搜过的点,那么这条边(和它的反向边)就是非树边
注意!:这里dfs的时候不能传来到当前节点的节点fa,而是要传来到这个条的边,否则在n=2的时候会找不到非树边
代码
void find_circle(int x,int pre){
vis[x]=1;
for(int i=fi[x];~i;i=p[i].nxt){
if(i==(pre^1)) continue;
int to=p[i].to;
if(vis[to]){
e=i;
u=x;v=to;continue;
}
find_circle(to,i);
}
}
练习
城市环路
骑士
两道很接近的较水的题
解决环套树后就变成没有上司的舞会了
环套树的直径
找出环上的所有点
首先考虑不经过环的路径对每个点跑一遍树形dp,求出每个点不包含环上的点的子树的直径
答案首先可以对这些直径取ma尝试作为答案
下面考虑经过环的路径
刚才dp时可以顺便求出连在每个环上点上的最长链len
那么经过环的最长路径就可以表示为:
l
e
n
i
+
l
e
n
j
+
d
i
s
t
(
i
,
j
)
len_i+len_j+dist(i,j)
leni+lenj+dist(i,j)
考虑求上面的最大值,可以破环成链,倍长后用单调队列优化解决
代码
(本题调来调去,代码有些屎山)
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
#define ll long long
#define I register int
ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-1;c=getchar();}
while(isdigit(c)){x=x*10+c-'0';c=getchar();}
return x*f;
}
int n;
struct node{
int to,nxt,v;
}p[N<<1];
int fi[N],cnt=-1;
void addline(int x,int y,int v){
p[++cnt]=(node){y,fi[x],v};
fi[x]=cnt;
}
ll ans;
int u,v,fa[N],fv[N];
bool vis[N];
int id[N],E=-1;
void find_circle(int x,int pre){
//printf("x=%d fa=%d\n",x,fa[x]);
vis[x]=1;
for(int i=fi[x];~i;i=p[i].nxt){
if(i==(pre^1)) continue;
if(i==E||i==(E^1)) continue;
int to=p[i].to;
if(vis[to]){
E=i;u=x;v=to;continue;
}
fa[to]=x;fv[to]=p[i].v;
find_circle(to,i);
}
}
ll dis[N],d[N];
void dfs(int x,int f){
ll mx=0,sec=0;d[x]=0;
for(int i=fi[x];~i;i=p[i].nxt){
int to=p[i].to;
if(to==f||id[to]){
//printf(" fail:x=%d to=%d\n",x,to);
continue;
}
dfs(to,x);
ll now=dis[to]+p[i].v;
//printf(" x=%d to=%d now=%lld\n",x,to,now);
if(now>mx) swap(now,mx);
if(now>sec) swap(now,sec);
d[x]=max(d[x],d[to]);
}
//printf("x=%d mx=%lld sec=%lld\n",x,mx,sec);
dis[x]=mx;d[x]=max(d[x],mx+sec);
}
int q[N],st,ed,len;
ll sum[N<<1];
ll val(int x,int now){
now--;
// printf("val:x=%d now=%d id=%d res=%lld\n",x,now,id[x],dis[x]+max(sum[now]-sum[id[x]-1],sum[len]-(sum[now])+sum[id[x]-1]));
return dis[x]+max(sum[now]-sum[id[x]-1],sum[len]-(sum[now])+sum[id[x]-1]);
}
ll a[N<<1],dd[N<<1];
void solve(int x){
find_circle(x,-2);
int uu=u;len=1;id[uu]=1;
while(uu!=v){
len++;
uu=fa[uu];id[uu]=len;
}
uu=u;ll res=0;
dfs(u,0);res=max(res,d[u]);
while(uu!=v){
uu=fa[uu];dfs(uu,0);
res=max(res,d[uu]);
}
for(uu=u;1;uu=fa[uu]){
//printf("u=%d dis=%lld d=%lld\n",uu,dis[uu],d[uu]);
if(uu==v) break;
}
for(uu=u;uu!=v;uu=fa[uu]){
a[id[uu]]=fv[uu];dd[id[uu]]=dis[uu];
sum[id[uu]]=sum[id[uu]-1]+fv[uu];
}
a[len]=p[E].v;sum[len]=sum[len-1]+p[E].v;dd[len]=dis[v];
for(int i=len+1;i<=2*len;i++){
a[i]=a[i-len];dd[i]=dd[i-len];
sum[i]=sum[i-1]+a[i];
}
//for(int i=1;i<=2*len;i++) printf("i=%d a=%lld sum=%lld dd=%lld\n",i,a[i],sum[i],dd[i]);
st=1,ed=1;q[1]=1;
for(int i=2;i<=2*len;i++){//printf("i=%d len=%d",i,len);
while(st<=ed&&i-q[st]>=len) st++;
//printf(" i=%d st=%d res=%lld+%lld+%lld-%lld\n",i,q[st],dd[q[st]],dd[i],sum[i],sum[q[st]]);
res=max(res,dd[q[st]]+dd[i]+sum[i-1]-sum[q[st]-1]);
while(st<=ed&&dd[q[ed]]-sum[q[ed]-1]<=dd[i]-sum[i-1]) ed--;
q[++ed]=i;
}
ans+=res;
// printf("x=%d res=%lld\n\n",x,res);
}
int main(){
memset(fi,-1,sizeof(fi));
n=read();
for(int i=1;i<=n;i++){
int x=read(),y=read();
addline(i,x,y);addline(x,i,y);
}
for(int i=1;i<=n;i++){
if(!vis[i]){
solve(i);
}
}
printf("%lld",ans);
}
/*
5
2 1
3 3
1 2
2 5
2 4
*/