日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

模板:环套树

發(fā)布時間:2023/12/3 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 模板:环套树 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 前言
  • 解析
    • 找環(huán)
      • 代碼
  • 練習
  • 環(huán)套樹的直徑
    • 代碼
  • thanks for reading!

前言

環(huán)套樹者,一個環(huán)套一棵樹也

解析

定義:n個點,n條邊的無向連通圖
其實就是樹多了一條邊,連出了一個環(huán)

性質:如果對環(huán)套樹進行dfs,多出的一條非樹邊一定是一條返祖邊

考慮dfs的過程如果它不是返祖邊,dfs的時候就會直接從這條邊過去,該邊就會成為樹邊了

找環(huán)

如何在環(huán)套樹上找到非樹邊?
考慮dfs,如果出邊指向已經(jīng)被搜過的點,那么這條邊(和它的反向邊)就是非樹邊
注意!:這里dfs的時候不能傳來到當前節(jié)點的節(jié)點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);} }

練習

城市環(huán)路
騎士
兩道很接近的較水的題
解決環(huán)套樹后就變成沒有上司的舞會了

環(huán)套樹的直徑

找出環(huán)上的所有點
首先考慮不經(jīng)過環(huán)的路徑對每個點跑一遍樹形dp,求出每個點不包含環(huán)上的點的子樹的直徑
答案首先可以對這些直徑取ma嘗試作為答案
下面考慮經(jīng)過環(huán)的路徑
剛才dp時可以順便求出連在每個環(huán)上點上的最長鏈len
那么經(jīng)過環(huán)的最長路徑就可以表示為:
leni+lenj+dist(i,j)len_i+len_j+dist(i,j)leni?+lenj?+dist(i,j)
考慮求上面的最大值,可以破環(huán)成鏈,倍長后用單調隊列優(yōu)化解決

代碼

(本題調來調去,代碼有些屎山)

#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 */

thanks for reading!

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結

以上是生活随笔為你收集整理的模板:环套树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。