原文鏈接https://www.cnblogs.com/zhouzhendong/p/CF980F.html
題目傳送門 - CF980F
題意
給定一個 $n$ 個節點 $m$ 條長為 $1$ 的邊的每個點最多只屬於一個環的仙人掌。
現在請你通過刪邊把仙人掌轉化成樹。
對於每一個點,輸出在所有不同的刪邊方案中, 距離該點最遠的點與他之間的距離值 的最小值。
$n\leq 5\times 10^5$
題解
首先,我們跑一跑 Tarjan ,找出每一個雙聯通分量。
然后我們把每一個雙聯通分量里面的點按照順序存在 vector 里面。
我們稱每一個環中連向環的父親環的節點為該環的 環根 。
然后我們考慮從下網上跑一跑 樹形DP ,處理出以每一個點為根的子仙人掌中所有不同的刪邊方式中最遠點距離的最小值。
這次 DP 結束后,我們記第 $i$ 個節點的結果為 $Deep_i$ 。其中,對於任意環根 $i$,特殊地, $Deep_i$ 的涉及范圍為以當前環為根的子仙人掌。
為了后面的方便,對於任意環根我們還需要記錄兩個值 : $spDeep_i,\ \ usDeep_i$ 。
具體含義見下圖:

對於環根 $x$ ,顯然有 $Deep_x=\max(spDeep_x,usDeep_x)$,要求 $spDeep_x$ 可以通過預處理前后綴 $\max$,然后枚舉該環的斷邊來得到。
下一步,我們需要處理一個 $Far_i$。
對於環根: $Far_i$ 表示下一步從該環根上行,最優情況下的最遠距離。
對於普通的點: $Far_i$ 表示下一步從該點向同環的點走,最優情況下的最遠距離。
只要處理出這個東西,則第 $i$ 個節點的答案就是 $\max(Far_i,Deep_i)$ 了。
我們考慮一下那些會對 $Far_i$ 有貢獻的節點。
第一種,當前點不是環根,則向同環的節點走會有貢獻。
第二種,當前點是環根,向同環的點和向父親走都會有貢獻。注意,這個向父親走的貢獻有兩種,一種是由父親的其他子節點來的,一種是由父親的同環節點來的(父親的 $Far$ )。
如果是環根,那么我們特殊處理即可。
現在考慮處理非環根的 $Far$ 。
通過證(感性)明(理解),我們可以發現,對於當前環,當起始點在被順時針遍歷的時候,最優方案下刪掉的邊也是 大致 順時針移動的。
我們可以維護當前點兩個方向的節點最大貢獻值盡量平衡來獲取最優解。
於是我們可以用兩個單調隊列來實現。這一部分是關鍵。需要把環鋪成三倍長度,並加上特定的權值。
這里具體不展開描述了,可以參見代碼。
我由於彈出單調隊列 QR 的時候少彈了一個位置, wa on test 18,找了5個多小時QAQ 。
代碼
#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=500005,M=N*4;
struct Gragh{
//2M*2*4B+0.5M*4B=18MB
int cnt,y[M],nxt[M],fst[N];
void clear(){
cnt=1;
memset(fst,0,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
}
}g,g2;
int n,m;
int dfn[N],low[N],inst[N],st[N],vis[N],id[N],Time,top,tot;
int used[M];
int Fa[N];
vector <int> cir[N],son[N];
void Tarjan_Prepare(){
Time=top=tot=0;
memset(id,0,sizeof id);
memset(st,0,sizeof st);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(vis,0,sizeof vis);
memset(inst,0,sizeof inst);
memset(used,0,sizeof used);
}
void Tarjan(int x){
dfn[x]=low[x]=++Time;
vis[x]=inst[x]=1;
st[++top]=x;
for (int i=g.fst[x];i;i=g.nxt[i]){
if (used[i/2])
continue;
used[i/2]=1;
if (!vis[g.y[i]]){
Tarjan(g.y[i]);
low[x]=min(low[x],low[g.y[i]]);
}
else if (inst[g.y[i]])
low[x]=min(low[x],low[g.y[i]]);
}
if (dfn[x]==low[x]){
tot++;
id[st[top]]=tot;
inst[st[top]]=0;
while (st[top--]!=x){
id[st[top]]=tot;
inst[st[top]]=0;
}
}
}
void Get_cir(int x){
if (vis[x])
return;
vis[x]=1;
cir[id[x]].push_back(x);
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (id[y]==id[x])
Get_cir(y);
}
}
void build(int x,int pre){
int ID=id[x];
Fa[ID]=pre;
cir[ID].clear();
Get_cir(x);
for (int i=0;i<cir[ID].size();i++){
int u=cir[ID][i];
son[u].clear();
for (int j=g.fst[u];j;j=g.nxt[j]){
int v=g.y[j];
if (id[v]==ID||id[v]==pre)
continue;
son[u].push_back(v);
build(v,ID);
}
}
}
int Deep[N];
// The Minimum value of the distance between
// x and the deepest posterity of x
// 環根: 以該環為根的子樹中,距離 x 最遠的點與其距離的 最小值
// 非環根: 該節點所有子節點(顯然都是環根)的 Deep 最大值 + 1
int spDeep[N],usDeep[N];
// spDeep[x] x 為環根,環邊節點給他的貢獻
// usDeep[x] x 為環根,橋邊子樹給他的貢獻
int Lmax[N],Rmax[N];
void Get_Deep(int x){
int ID=id[x];
for (int i=0;i<cir[ID].size();i++){
int u=cir[ID][i],v;
Deep[u]=0;
for (int j=0;j<son[u].size();j++){
v=son[u][j];
Get_Deep(v);
Deep[u]=max(Deep[u],Deep[v]+1);
}
}
int n=cir[ID].size()-1;
Lmax[0]=Rmax[n+1]=0;
for (int i=1;i<=n;i++)
Lmax[i]=max(Lmax[i-1],Deep[cir[ID][i]]+i);
for (int i=n;i>=1;i--)
Rmax[i]=max(Rmax[i+1],Deep[cir[ID][i]]+(n-i+1));
int v=1e9;
for (int i=1;i<=n+1;i++)
v=min(v,max(Lmax[i-1],Rmax[i]));
Deep[x]=max(usDeep[x]=Deep[x],spDeep[x]=v);
}
int Far[N];
// The Minimum value of the distance between
// x and the farthest vetrex which isn't a posterity of x
// 環根: 由其父親繼承
// 非環根: 環內單調隊列跑出來
int val[N*3];
int Lv[N*3],Rv[N*3];
struct Monotone_Queue{
int head,tail,q[N*3],d[N*3];
void clear(){
head=1,tail=0;
}
int front(){
return head<=tail?q[head]:-1e9;
}
void push(int v,int _d){
while (head<=tail&&q[tail]<=v)
tail--;
q[++tail]=v,d[tail]=_d;
}
void pop(int _d){
while (head<=tail&&d[head]<_d)
head++;
}
int Next_front(int _d){
if (head>tail)
return -1e9;
if (d[head]!=_d)
return q[head];
return head==tail?-1e9:q[head+1];
}
}QL,QR;
vector <int> LRmax[N];
void Solve(int x,int far){
int ID=id[x];
Far[x]=far;
int n=cir[ID].size();
for (int i=1;i<n;i++)
val[i]=Deep[cir[ID][i]];
val[0]=max(usDeep[x],Far[x]);
for (int i=n;i<n*3;i++)
val[i]=val[i%n];
for (int i=0;i<n*3;i++){
Lv[i]=val[i]+n*3-i;
Rv[i]=val[i]+i+1;
}
QL.clear(),QR.clear();
int Lp=2,Rp=n+1;
for (int i=Lp;i<=n;i++)
QL.push(Lv[i],i);
for (int i=n+1;i<n*2;i++){
int result=1e9;
int v1=n*3-i,v2=i+1;
while (Rp<i){
Lp++,QL.pop(Lp);
Rp++;
if (Rp!=i)
QR.push(Rv[Rp],Rp);
}
while (QL.Next_front(Lp)-v1>max(QR.front(),Rv[Rp+1])-v2){
result=min(result,QL.front()-v1);
Lp++,QL.pop(Lp);
Rp++,QR.push(Rv[Rp],Rp);
}
result=min(result,QL.front()-v1);
result=min(result,max(QR.front(),Rv[Rp+1])-v2);
QL.push(Lv[i],i);
QR.pop(i+2);
Far[cir[ID][i%n]]=result;
}
for (int i=0;i<n;i++){
int u=cir[ID][i];
int m=son[u].size();
Lmax[0]=Rmax[m+1]=0;
for (int j=1;j<=m;j++)
Lmax[j]=max(Lmax[j-1],Deep[son[u][j-1]]+1);
for (int j=m;j>=1;j--)
Rmax[j]=max(Rmax[j+1],Deep[son[u][j-1]]+1);
LRmax[u].clear();
for (int j=0;j<m;j++)
LRmax[u].push_back(max(Lmax[j],Rmax[j+2]));
for (int j=0;j<m;j++)
Solve(son[u][j],max(spDeep[u],max(Far[u],LRmax[u][j]))+1);
}
}
int main(){
scanf("%d%d",&n,&m);
g.clear();
for (int i=1,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
g.add(a,b);
g.add(b,a);
}
Tarjan_Prepare();
for (int i=1;i<=n;i++)
if (!vis[i])
Tarjan(i);
memset(vis,0,sizeof vis);
build(1,0);
Get_Deep(1);
Solve(1,0);
for (int i=1;i<=n;i++)
printf("%d ",max(Far[i],Deep[i]));
return 0;
}
再放一份打滿調試語句的代碼:
#include <bits/stdc++.h>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=500005,M=N*4;
struct Gragh{
//2M*2*4B+0.5M*4B=18MB
int cnt,y[M],nxt[M],fst[N];
void clear(){
cnt=1;
memset(fst,0,sizeof fst);
}
void add(int a,int b){
y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
}
}g,g2;
int n,m;
int dfn[N],low[N],inst[N],st[N],vis[N],id[N],Time,top,tot;
int used[M];
int Fa[N];
vector <int> cir[N],son[N];
void Tarjan_Prepare(){
Time=top=tot=0;
memset(id,0,sizeof id);
memset(st,0,sizeof st);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
memset(vis,0,sizeof vis);
memset(inst,0,sizeof inst);
memset(used,0,sizeof used);
}
void Tarjan(int x){
dfn[x]=low[x]=++Time;
vis[x]=inst[x]=1;
st[++top]=x;
for (int i=g.fst[x];i;i=g.nxt[i]){
if (used[i/2])
continue;
used[i/2]=1;
if (!vis[g.y[i]]){
Tarjan(g.y[i]);
low[x]=min(low[x],low[g.y[i]]);
}
else if (inst[g.y[i]])
low[x]=min(low[x],low[g.y[i]]);
}
if (dfn[x]==low[x]){
tot++;
id[st[top]]=tot;
inst[st[top]]=0;
while (st[top--]!=x){
id[st[top]]=tot;
inst[st[top]]=0;
}
}
}
void Get_cir(int x){
if (vis[x])
return;
vis[x]=1;
cir[id[x]].push_back(x);
for (int i=g.fst[x];i;i=g.nxt[i]){
int y=g.y[i];
if (id[y]==id[x])
Get_cir(y);
}
}
void build(int x,int pre){
int ID=id[x];
Fa[ID]=pre;
cir[ID].clear();
Get_cir(x);
for (int i=0;i<cir[ID].size();i++){
int u=cir[ID][i];
son[u].clear();
for (int j=g.fst[u];j;j=g.nxt[j]){
int v=g.y[j];
if (id[v]==ID||id[v]==pre)
continue;
son[u].push_back(v);
build(v,ID);
}
}
}
int Deep[N];
// The Minimum value of the distance between
// x and the deepest posterity of x
// 環根: 以該環為根的子樹中,距離 x 最遠的點與其距離的 最小值
// 非環根: 該節點所有子節點(顯然都是環根)的 Deep 最大值 + 1
int spDeep[N],usDeep[N];
// spDeep[x] x 為環根,環邊節點給他的貢獻
// usDeep[x] x 為環根,橋邊子樹給他的貢獻
int Lmax[N],Rmax[N];
void Get_Deep(int x){
int ID=id[x];
for (int i=0;i<cir[ID].size();i++){
int u=cir[ID][i],v;
Deep[u]=0;
for (int j=0;j<son[u].size();j++){
v=son[u][j];
Get_Deep(v);
Deep[u]=max(Deep[u],Deep[v]+1);
}
}
int n=cir[ID].size()-1;
Lmax[0]=Rmax[n+1]=0;
for (int i=1;i<=n;i++)
Lmax[i]=max(Lmax[i-1],Deep[cir[ID][i]]+i);
for (int i=n;i>=1;i--)
Rmax[i]=max(Rmax[i+1],Deep[cir[ID][i]]+(n-i+1));
int v=1e9;
for (int i=1;i<=n+1;i++)
v=min(v,max(Lmax[i-1],Rmax[i]));
Deep[x]=max(usDeep[x]=Deep[x],spDeep[x]=v);
}
int Far[N];
// The Minimum value of the distance between
// x and the farthest vetrex which isn't a posterity of x
// 環根: 由其父親繼承
// 非環根: 環內單調隊列跑出來
int val[N*3];
int Lv[N*3],Rv[N*3];
struct Monotone_Queue{
int head,tail,q[N*3],d[N*3];
void clear(){
head=1,tail=0;
}
int front(){
return head<=tail?q[head]:-1e9;
}
void push(int v,int _d){
while (head<=tail&&q[tail]<=v)
tail--;
q[++tail]=v,d[tail]=_d;
}
void pop(int _d){
while (head<=tail&&d[head]<_d)
head++;
}
int Next_front(int _d){
if (head>tail)
return -1e9;
if (d[head]!=_d)
return q[head];
return head==tail?-1e9:q[head+1];
}
void print(){
printf("size=%d, sit=:",tail-head+1);
for (int i=head;i<=tail;i++)
printf("(%d,%d) ",q[i],d[i]);
puts("");
}
}QL,QR;
vector <int> LRmax[N];
void Solve(int x,int far){
int ID=id[x];
Far[x]=far;
int n=cir[ID].size();
for (int i=1;i<n;i++)
val[i]=Deep[cir[ID][i]];
val[0]=max(usDeep[x],Far[x]);
for (int i=n;i<n*3;i++)
val[i]=val[i%n];
for (int i=0;i<n*3;i++){
Lv[i]=val[i]+n*3-i;
Rv[i]=val[i]+i+1;
}
// for (int i=0;i<n*3;i++)
// printf("==>%d %d(%d) %d(%d)\n",val[i],Lv[i],n*3-i,Rv[i],i+1);
QL.clear(),QR.clear();
int Lp=2,Rp=n+1;
for (int i=Lp;i<=n;i++)
QL.push(Lv[i],i);
for (int i=n+1;i<n*2;i++){
int result=1e9;
int v1=n*3-i,v2=i+1;
// printf("i=%d,v1=%d,v2=%d\n",i,v1,v2);
while (Rp<i){
Lp++,QL.pop(Lp);
Rp++;
if (Rp!=i)
QR.push(Rv[Rp],Rp);
}
// printf("x=%d,i=%d,Lp=%d,Rp=%d,Q=..(L,R)\n",x,i,Lp,Rp);QL.print(),QR.print();
while (QL.Next_front(Lp)-v1>max(QR.front(),Rv[Rp+1])-v2){
result=min(result,QL.front()-v1);
Lp++,QL.pop(Lp);
Rp++,QR.push(Rv[Rp],Rp);
}
// printf("x=%d,i=%d,Lp=%d,Rp=%d,Q=..(L,R)\n",x,i,Lp,Rp);QL.print(),QR.print();
result=min(result,QL.front()-v1);
result=min(result,max(QR.front(),Rv[Rp+1])-v2);
QL.push(Lv[i],i);
QR.pop(i+2);
Far[cir[ID][i%n]]=result;
}
for (int i=0;i<n;i++){
int u=cir[ID][i];
int m=son[u].size();
Lmax[0]=Rmax[m+1]=0;
for (int j=1;j<=m;j++)
Lmax[j]=max(Lmax[j-1],Deep[son[u][j-1]]+1);
for (int j=m;j>=1;j--)
Rmax[j]=max(Rmax[j+1],Deep[son[u][j-1]]+1);
/* printf("u=%d,son=:",u);
for (int j=0;j<m;j++)
printf("%d ",son[u][j]);
for (int i=1;i<=m;i++)
printf ("(%d,%d) ",Lmax[i],Rmax[i]);puts("");*/
LRmax[u].clear();
for (int j=0;j<m;j++)
LRmax[u].push_back(max(Lmax[j],Rmax[j+2]));
for (int j=0;j<m;j++)
Solve(son[u][j],max(spDeep[u],max(Far[u],LRmax[u][j]))+1);
}
}
int main(){
scanf("%d%d",&n,&m);
g.clear();
for (int i=1,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
g.add(a,b);
g.add(b,a);
}
Tarjan_Prepare();
for (int i=1;i<=n;i++)
if (!vis[i])
Tarjan(i);
memset(vis,0,sizeof vis);
// for (int i=1;i<=n;i++)
// printf("%d: %d\n",i,id[i]);
build(1,0);
Get_Deep(1);
Solve(1,0);
/* for (int i=1;i<=tot;i++,puts(""))
for (int j=0;j<cir[i].size();j++)
printf("%d ",cir[i][j]);*/
/* for (int i=1;i<=n;i++){
printf("%d:",i);
for (int j=0;j<son[i].size();j++)
printf(" %d",son[i][j]);
puts("");
}*/
/* for (int i=1;i<=n;i++)
printf("%d: %d %d %d %d\n",i,Deep[i],spDeep[i],usDeep[i],Far[i]);*/
for (int i=1;i<=n;i++)
printf("%d ",max(Far[i],Deep[i]));
return 0;
}
