[XSY] 分割(dfs树)
分割
題目相當(dāng)于問 刪掉兩個(gè)點(diǎn)后 圖是否仍然連通
割點(diǎn)問題,考慮用dfs樹解決
設(shè)刪去點(diǎn)u,v(dfn[v]<dfn[u])
把 u, v 刪去之后整棵樹大概斷成了幾個(gè)部分:
? v 上面到根的部分,以及上面掛著的那些東西,記作 highhighhigh;
? u 到 v 之間的那一段,以及上面掛著的那些東西,記作 midmidmid;
? u ,v的子樹們,記作 lowulow_ulowu?,lowvlow_vlowv?。
要保證刪去u,v后圖連通,則lowulow_ulowu?,midmidmid,lowvlow_vlowv?必須想辦法和highhighhigh連通
lowvlow_vlowv?:每顆子樹有非樹邊連接highhighhigh
lowulow_ulowu?:每顆子樹 有非樹邊連接highhighhigh 或 有非樹邊連接midmidmid且midmidmid有非樹邊連接highhighhigh
midmidmid:有非樹邊連接highhighhigh 或 有非樹邊連接lowulow_ulowu?的其中一顆子樹,同時(shí)這顆子樹有非樹邊連接highhighhigh
當(dāng)然,fa[u]==vfa[u]==vfa[u]==v的情況略有不同,且v==1v==1v==1時(shí)要特判
上面的思路除了 加粗字體的情況 沒考慮到,其它都想到了
以下是想不出來的實(shí)現(xiàn):
對(duì)于 子樹內(nèi)有非樹邊連接highhighhigh 的條件:
預(yù)處理出 low[u] 表示 以u(píng)為根的子樹通過非樹邊最高能連到的位置
對(duì)于 midmidmid有非樹邊連接highhighhigh 的條件:
預(yù)處理出 g[u][k] 表示 u到其2k2^k2k次祖先 的這一段以及上面掛著的東西中,不算 u,通過非樹邊最高能到的位置(樹上倍增)
(注意:g[u][0]!=low[fa[u]],詳見代碼)
對(duì)于 lowulow_ulowu?中有非樹邊連接midmidmid 的條件:
考慮轉(zhuǎn)化一下條件,
原條件為 對(duì)于lowulow_ulowu?的任意一顆子樹,若在[1,dfn[v]-1]中沒有通過非樹邊能連到的位置,那么在[dfn[v]+1,dfn[u]-1]中至少有一個(gè)通過非樹邊能連到的位置
我們將其轉(zhuǎn)化為 若lowulow_ulowu?中有一顆子樹在[1,dfn[v]-1]和[dfn[v]+1,dfn[u]-1]中均沒有通過非樹邊能連到的位置,那么邊(u,v)不能刪
如圖,w為u的其中一顆子樹,標(biāo)紫的點(diǎn)是w通過非樹邊能連到的位置中在u上方的部分,可以發(fā)現(xiàn),只有v在low[w]及其往上的部分時(shí),[1,dfn[v]-1]中才會(huì)沒有w通過非樹邊能連到的位置,這時(shí)若[dfn[v]+1,dfn[u]-1]中沒有其它w通過非樹邊能連到的位置,即dfn[w通過非樹邊第二高能連到的位置]>=dfn[u],則(u,v)不能刪
預(yù)處理出 slow[u] 表示 以u(píng)為根的子樹通過非樹邊第二高能連到的位置即可
(同學(xué)教的,妙啊!)
對(duì)于 midmidmid有非樹邊連接lowulow_ulowu?的其中一顆子樹,同時(shí)這顆子樹有非樹邊連接highhighhigh 的條件:
還是考慮轉(zhuǎn)化條件,
原條件為 lowulow_ulowu?中有一顆子樹,在[1,dfn[v]-1]和[dfn[v]+1,dfn[u]-1]中均有其通過非樹邊能連到的位置
轉(zhuǎn)化為 對(duì)于lowulow_ulowu?的其中一顆子樹,記l,rl,rl,r為這棵子樹通過非樹邊能連到的位置中在u上方部分的最高點(diǎn)和最低點(diǎn),若 lll<=dfn[v]-1 且 rrr>=dfn[v]+1,即lll+1<=dfn[v]<=rrr-1,那么(u,v)邊的midmidmid部分一定能通過連接這顆子樹連到highhighhigh
具體地,lll即為low[son[u]],
對(duì)每個(gè)點(diǎn)u維護(hù)一個(gè)優(yōu)先隊(duì)列,記錄以u(píng)為根的子樹通過非樹邊能連到的位置,pop出 son[u] 的優(yōu)先隊(duì)列中 dfn值>=dfn[u] 的位置即可找到rrr(優(yōu)先隊(duì)列合并)
對(duì)于最后兩個(gè)條件,我一開始想的是暴力維護(hù)每個(gè)點(diǎn)u即它的子樹通過非樹邊能連到的所有位置(以dfn序?yàn)橄聵?biāo)建線段樹,維護(hù)區(qū)間內(nèi)能連接到的位置的個(gè)數(shù)(類似權(quán)值線段樹的感覺)),然后合并線段樹
雖然后來了解到合并線段樹的時(shí)間復(fù)雜度是O(nlogn)O(nlogn)O(nlogn),所以整道題的復(fù)雜度也就O(nlog2n)O(nlog^2n)O(nlog2n),即理論復(fù)雜度也是對(duì)的,但是常數(shù)過大,大概率會(huì)T
但是syh大佬通過把 判斷對(duì)于確定的u,v,(u,v)是否可刪 轉(zhuǎn)化為 對(duì)于確定的u,尋找什么樣的v滿足(u,v) 可刪/不可刪,讓這題有了簡(jiǎn)單許多的做法 OrzOrzOrz
Code:
#include<iostream> #include<cstdio> #include<map> #include<algorithm> #include<vector> #include<queue> using namespace std; const int N=3e5+5; struct data{int v,nxt; }edge[N<<1]; vector<int> vex[N],vid[N]; int n,m,cnt,head[N],ind,dfn[N],sz[N],son[N],fa[N][21]; int low[N],slow[N],g[N][21]; int st[N<<2]; int fl[N],hk[N]; //fl[u]=刪掉u后,有幾個(gè)u的子節(jié)點(diǎn)會(huì)被與連通圖斷開 //hk[v]=刪掉fa[v][0]后,v會(huì)與連通圖斷開 map<int,int> mp[N]; //mp[a][b]=1表示為保持圖連通,若刪了a,則b不能被刪 bool fla[N],flagg[N]; int rt[N]; priority_queue<int> q[N]; queue<int> que; void add(int u,int v){edge[++cnt].v=v;edge[cnt].nxt=head[u];head[u]=cnt; } void merge(int a,int b){int p[11];p[1]=low[a];p[2]=slow[a];p[3]=low[b];p[4]=slow[b];sort(p+1,p+5);low[a]=p[1];slow[a]=n+1;for(int i=2;i<=4;i++){if(p[i]!=p[i-1]){slow[a]=p[i];break;}} } inline void dfs(int u){ind++;dfn[u]=ind;for(int i=head[u];i;i=edge[i].nxt){if(!dfn[edge[i].v]){fa[edge[i].v][0]=u;dfs(edge[i].v);} } } void dfs_tree(int u){//判l(wèi)ow直接與high連通+low通過mid與high連通 sz[u]=1;low[u]=dfn[u];slow[u]=n+1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u][0]) continue;if(fa[v][0]==u){son[u]++;dfs_tree(v);merge(u,v);sz[u]=sz[u]+sz[v];if(low[v]>=dfn[u]){fl[u]++;hk[v]++;}else if(slow[v]>=dfn[u]) mp[u][low[v]]=1;}else{if(dfn[v]==low[u]) continue;if(low[u]>dfn[v]){slow[u]=low[u];low[u]=dfn[v];}else if(slow[u]>dfn[v]) slow[u]=dfn[v];}} } //此時(shí)的線段樹用于統(tǒng)計(jì)low的最小值 inline void update(int u,int l,int r,int x,int v){if(l>x||r<x)return;if(l==r){st[u]=v;return;}int mid=(l+r)/2;update(u<<1,l,mid,x,v);update(u<<1|1,mid+1,r,x,v);st[u]=min(st[u<<1],st[u<<1|1]); } inline int query(int u,int l,int r,int ql,int qr){if(l>qr||r<ql)return n+1;if(ql<=l&&r<=qr)return st[u];int mid=(l+r)/2;int res1=query(u<<1,l,mid,ql,qr);int res2=query(u<<1|1,mid+1,r,ql,qr);return min(res1,res2); } //此時(shí)的線段樹用于維護(hù)區(qū)間是否可選 inline void modify(int u,int l,int r,int ql,int qr){if(!flagg[u]){que.push(u);flagg[u]=1;}if(l>qr||r<ql)return;if(ql<=l&&r<=qr){st[u]=1;return;}int mid=(l+r)/2;modify(u<<1,l,mid,ql,qr);modify(u<<1|1,mid+1,r,ql,qr);st[u]=st[u<<1]&st[u<<1|1]; } inline bool ask(int u,int l,int r,int x){if(l>x||r<x) return 0;if(st[u]) return st[u];if(l==r) return st[u];int mid=(l+r)/2;return ask(u<<1,l,mid,x)|ask(u<<1|1,mid+1,r,x); } inline void get_g(int u){//判mid不通過low直接與high連通 for(int i=1;i<=20;i++){fa[u][i]=fa[fa[u][i-1]][i-1];g[u][i]=min(g[u][i-1],g[fa[u][i-1]][i-1]);}int sum=0,tot=0,s=n+1;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u][0]) continue;if(fa[v][0]==u) sum++;else s=min(s,dfn[v]);}for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(fa[v][0]==u){tot++;update(1,1,sum,tot,low[v]);}}tot=0;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(fa[v][0]==u){tot++;g[v][0]=min(min(query(1,1,sum,1,tot-1),query(1,1,sum,tot+1,sum)),s);}}for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(fa[v][0]==u) get_g(v);} } int solve(int u,int v){int s=n+1;for(int i=20;i>=0;i--){if(dfn[fa[v][i]]>dfn[u]){s=min(s,g[v][i]);v=fa[v][i];}}return s; } int mergeq(int a,int b){if(q[a].size()<q[b].size()) swap(a,b);while(!q[b].empty()){q[a].push(q[b].top());q[b].pop();}return a; } inline void getans(int u){//判mid通過low與high連通 for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(fa[v][0]==u) getans(v);} rt[u]=u;for(int i=head[u];i;i=edge[i].nxt){int v=edge[i].v;if(v==fa[u][0])continue;if(fa[v][0]==u){int l=low[v],r=0;while(!q[rt[v]].empty()){if(q[rt[v]].top()>=dfn[u]) q[rt[v]].pop();else{r=q[rt[v]].top();break;}}modify(1,1,n,l+1,r-1);//對(duì)于非樹邊(u,y)(dfn[y]<dfn[u]),若dfn[y]在[l+1,r-1]的區(qū)間內(nèi),則刪去此邊后(u,y)邊的mid部分仍與high連通 rt[u]=mergeq(rt[v],rt[u]);}else{q[rt[u]].push(dfn[v]);}}for(int i=0;i<vex[u].size();i++){int x=u;int y=vex[u][i];int z=vid[u][i];if(dfn[y]>dfn[x])continue;if(x==1||y==1)continue;if(y==fa[x][0])continue;if(!ask(1,1,n,dfn[y]))fla[z]=1;}while(!que.empty()){flagg[que.front()]=0;st[que.front()]=0;que.pop();}//恢復(fù) } char check(int id,int a,int b){if(dfn[a]>dfn[b]) swap(a,b);if(a==1){if(fa[b][0]==a){if(son[a]>2) return '1';if(son[a]>1&&sz[b]>1) return '1';if(son[b]>1) return '1';return '0';}if(son[a]>1) return '1';if(mp[b][dfn[a]]==0&&fl[b]==0) return '0';return '1';}if(fa[b][0]==a){if(sz[b]==1&&hk[b]==1){if(fl[a]<=1&&fl[b]==0&&mp[b][dfn[a]]==0) return '0';}else{if(fl[a]==0&&fl[b]==0&&mp[b][dfn[a]]==0) return '0';}return '1';}if(fl[a]||fl[b]||mp[b][dfn[a]]) return '1';int s=solve(a,b);if(s>=dfn[a]&&fla[id]) return '1';return '0'; } int main(){scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){int x,y;scanf("%d%d",&x,&y);add(x,y);add(y,x);vex[x].push_back(y);vex[y].push_back(x);vid[x].push_back(i);vid[y].push_back(i);}dfs(1);dfs_tree(1);get_g(1);for(int i=1;i<=n*4;i++) st[i]=0;getans(1);for(int i=1;i<=m;i++)putchar(check(i,edge[i*2-1].v,edge[i*2].v)); }總結(jié)
以上是生活随笔為你收集整理的[XSY] 分割(dfs树)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 西宁有什么好玩的 西宁有哪些好玩的地方
- 下一篇: [XSY] 相似(DP套DP)