文章目录
- 前言
- 解析
- 找环
- 代码
- 练习
- 环套树的直径
- 代码
- thanks for reading!
前言
环套树者,一个环套一棵树也
解析
定义: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
那么经过环的最长路径就可以表示为:
leni+lenj+dist(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
*/