文章目录

  • 小模板
    • 强联通分量
    • 割点
    • 边双连通分量
    • 点双连通分量
  • 支配树
    • CodeChef - GRAPHCNT
    • 3281: 小P的烦恼
    • test2018.3.3:problem C
  • 仙人掌和圆方树
    • bzoj2125: 最短路
    • bzoj4316: 小C的独立集
    • bzoj1023: [SHOI2008]cactus仙人掌图

小模板

强联通分量

luoguP3387【模板】缩点
有向图,强联通分量中的点两两可以互相到达。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=1e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,v[N],val[N];template<typename T> void read(T &x) {char ch=getchar(); T f=1; x=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}int ecnt,fir[N],nxt[N],to[N];
void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;
}int sta[N],top,dfk,dfn[N],low[N],bl[N],tot;
void tarjan(int x) {sta[++top]=x;dfn[x]=low[x]=++dfk;for(int i=fir[x];i;i=nxt[i]) {if(!dfn[to[i]]) {tarjan(to[i]);low[x]=min(low[x],low[to[i]]);}else if(!bl[to[i]]) low[x]=min(low[x],dfn[to[i]]);}if(dfn[x]==low[x]) {++tot;while(top) {int y=sta[top--];bl[y]=tot;val[tot]+=v[y];if(y==x) break;}}
}int in[N];
vector<int>vc[N];
queue<int>que;
void Add(int u,int v) {vc[u].push_back(v); in[v]++;
}int dp[N];
void tpsort() {For(x,1,n) for(int i=fir[x];i;i=nxt[i]) if(bl[x]!=bl[to[i]]) Add(bl[x],bl[to[i]]);For(x,1,tot) if(!in[x]) { dp[x]=val[x]; que.push(x); }while(!que.empty()) {int x=que.front();que.pop();int up=vc[x].size();For(i,0,up-1) {int y=vc[x][i];in[y]--;dp[y]=max(dp[y],dp[x]+val[y]);if(!in[y]) que.push(y);}}int ans=dp[1];For(i,1,tot) ans=max(ans,dp[i]);printf("%d\n",ans);
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(n); read(m);For(i,1,n) read(v[i]);For(i,1,m) {int u,v;read(u); read(v);add(u,v);}For(i,1,n) if(!dfn[i]) tarjan(i);tpsort();Formylove;
}

割点

luoguP3388 【模板】割点(割顶)
割点:无向图,去掉这个点会增加联通块的数目。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,cut[N];template<typename T> void read(T &x) {char ch=getchar(); T f=1; x=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}int ecnt,fir[N],nxt[N],to[N];
void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}int dfk,dfn[N],low[N],rtson;
void tarjan(int x,int RT) {dfn[x]=low[x]=++dfk;for(int i=fir[x];i;i=nxt[i]) {if(!dfn[to[i]]) {tarjan(to[i],RT);low[x]=min(low[x],low[to[i]]);if(x!=RT&&low[to[i]]>=dfn[x]) cut[x]=1;else if(x==RT) rtson++;}else low[x]=min(low[x],dfn[to[i]]);}if(x==RT&&rtson>1) cut[x]=1;
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(n); read(m);For(i,1,m) {int u,v;read(u); read(v);add(u,v);}For(i,1,n) if(!dfn[i]) { rtson=0; tarjan(i,i); }int ans=0;For(i,1,n) if(cut[i]) ans++;printf("%d\n",ans);For(i,1,n) if(cut[i]) printf("%d ",i); puts("");Formylove;
}

无向图,去掉这条边后联通块数目增加。
ZOJ - 2588 Burning Bridges
处理重边,记录下每个点的父亲边,且不用这条边更新我的dfn,最后若我的dfn=low,我的父亲边就是桥。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int T,n,m,cut[N];template<typename T> void read(T &x) {char ch=getchar(); T f=1; x=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}int ecnt,fir[N],nxt[N],to[N],id[N];
void add(int u,int v,int i) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; id[ecnt]=i;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; id[ecnt]=i;
}int dfk,dfn[N],low[N],feg[N];
void tarjan(int x) {dfn[x]=low[x]=++dfk;for(int i=fir[x];i;i=nxt[i]) {if(!dfn[to[i]]) {feg[to[i]]=i;tarjan(to[i]);low[x]=min(low[x],low[to[i]]);}else if(id[i]!=id[feg[x]]) low[x]=min(low[x],dfn[to[i]]);}if(dfn[x]==low[x]) cut[id[feg[x]]]=1;
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(T);while(T--) {read(n); read(m);For(i,1,m) {int u,v;read(u); read(v);add(u,v,i);}For(i,1,n) if(!dfn[i]) tarjan(i);int ans=0;For(i,1,m) if(cut[i]) ans++;printf("%d\n",ans);For(i,1,m) if(cut[i]) {ans--;if(ans) printf("%d ",i);else printf("%d\n",i);}if(T) {puts("");ecnt=dfk=0;memset(fir,0,sizeof(fir));memset(cut,0,sizeof(cut));memset(dfn,0,sizeof(dfn));}}Formylove;
}

边双连通分量

对于一个无向图的子图,当删除其中任意一条边后,不改变图内点的连通性,这样的子图叫做边的双连通子图。而当子图的边数达到最大时,叫做边的双连通分量。

显然求出桥后把所有桥删掉剩下的就是边双连通分量了。把dfs到的点入栈,每次当我的父亲边是桥时在栈中的所有点是一个边双,全部弹出即可。

hihocoder#1184 : 连通性二·边的双连通分量
每个边双的编号为边双中点的最小标号,输出边双的数目和每个点所在边双的编号。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int T,n,m,cut[N];template<typename T> void read(T &x) {char ch=getchar(); T f=1; x=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}int ecnt=1,fir[N],nxt[N],to[N];
void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}int dfk,dfn[N],low[N],sta[N],top,tot,bl[N];
void tarjan(int x,int fe) {sta[++top]=x;dfn[x]=low[x]=++dfk;for(int i=fir[x];i;i=nxt[i]) if((fe^1)!=i) {if(!dfn[to[i]]) {tarjan(to[i],i);low[x]=min(low[x],low[to[i]]);}else low[x]=min(low[x],dfn[to[i]]);}if(dfn[x]==low[x]) {tot++;int id=x;for(int i=top;i;i--) {int y=sta[i];id=min(id,y);if(y==x) break;}while(top) {int y=sta[top--];bl[y]=id;if(y==x) break;}}
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(n); read(m);For(i,1,m) {int u,v;read(u); read(v);add(u,v);}For(i,1,n) if(!dfn[i]) tarjan(i,0);printf("%d\n",tot);For(i,1,n) printf("%d ",bl[i]); puts("");Formylove;
}

点双连通分量

对于一个无向图的子图,当删除其中任意一个点后,不改变图内点的连通性,这样的子图叫做点的双连通子图。而当子图的边数达到最大时,叫做点的双连通分量。

割点将图分为若干点双,每个割点可能属于多个点双,但每条边仅属于一个点双。把边入队,每次找到一个割点,当前栈里的边属于同一个点双,弹出即可。每条边仅入队一次。

hihocoder#1190 : 连通性·四
每个点双的编号为点双中边的最小标号,输出点双的数目和每条边所在点双的编号。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int T,n,m,cut[N];template<typename T> void read(T &x) {char ch=getchar(); T f=1; x=0;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}int ecnt=1,fir[N],nxt[N],to[N];
void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;
}int dfk,dfn[N],low[N],sta[N],top,tot,bl[N],vis[N];
void tarjan(int x) {dfn[x]=low[x]=++dfk;for(int i=fir[x];i;i=nxt[i]) {if(vis[i]) continue;if(!dfn[to[i]]) {sta[++top]=i;vis[i]=vis[i^1]=1;tarjan(to[i]);low[x]=min(low[x],low[to[i]]);if(low[to[i]]>=dfn[x]) {tot++;int id=i/2;for(int j=top;j;j--) {int y=sta[j];id=min(id,y/2);if(y==i) break; }while(top) {int y=sta[top--];bl[y/2]=id;if(y==i) break;}}}else {sta[++top]=i;vis[i]=vis[i^1]=1;low[x]=min(low[x],dfn[to[i]]);}}
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(n); read(m);For(i,1,m) {int u,v;read(u); read(v);add(u,v);}For(i,1,n) if(!dfn[i]) tarjan(i);printf("%d\n",tot);For(i,1,m) printf("%d ",bl[i]); puts("");Formylove;
}

支配树

有向图,去掉x就无法从起点到y,x就是y的支配点。
一个非常详细的讲解:どこでもドア

CodeChef - GRAPHCNT

传送门
给定有向图,求无序点对(x,y)满足存在从1到x的路径和1到y的路径使两条路径仅有1这个交点。
即问1为根的支配树上lca为1的点对树。模板题。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=5e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,SZ[N];template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}vector<int>eg[N],vc[N];
int ecnt,fir[N],nxt[N],to[N];
void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;eg[v].push_back(u);
}int fa[N],fg[N],sdom[N],idom[N];
int find(int x) {if(x==fa[x]) return x;int    nf=find(fa[x]);if(sdom[fg[fa[x]]]<sdom[fg[x]]) fg[x]=fg[fa[x]];fa[x]=nf; return nf;
}int qry(int x) {find(x);return fg[x];
}int dfk,dfn[N],p[N],tid[N];
void dfs(int x,int Fa) {fa[x]=fg[x]=x;p[x]=Fa;dfn[x]=++dfk;tid[dfk]=x;sdom[x]=dfn[x];for(int i=fir[x];i;i=nxt[i]) if(!dfn[to[i]]) dfs(to[i],x);
}void build() {//For(i,1,n) fa[i]=fg[i]=i;dfs(1,0);Rep(i,dfk,2) {int x=tid[i],F=p[x],up=eg[x].size();For(j,0,up-1) {int y=eg[x][j];if(dfn[y]) sdom[x]=min(sdom[x],sdom[qry(y)]);}vc[tid[sdom[x]]].push_back(x);fa[x]=F;up=vc[F].size();For(j,0,up-1) {int w=vc[F][j];int v=qry(w);idom[w]=(sdom[v]==sdom[w]?F:v);}}For(i,2,dfk) { int x=tid[i]; idom[x]=(idom[x]==tid[sdom[x]]?idom[x]:idom[idom[x]]); }For(i,2,dfk) { int x=tid[i]; sdom[x]=tid[sdom[x]]; }
}int main() {//freopen("1.in","r",stdin);//freopen("1.out","w",stdout);read(n); read(m);For(i,1,m) {int x,y;read(x); read(y);add(x,y);}build();LL ans=0;// (LL)n*(n-1)/2;Rep(i,dfk,1) {int x=tid[i];SZ[x]++;SZ[idom[x]]+=SZ[x];if(idom[x]==1) ans-=(LL)SZ[x]*(SZ[x]-1)/2;else if(x==1)ans+=(LL)SZ[x]*(SZ[x]-1)/2;}   printf("%lld\n",ans);Formylove;
}

3281: 小P的烦恼

传送门
从s到t的路径上的必经边为特殊边。
用两条长度为L的线去覆盖图上的边使未被覆盖的特殊边的长度最短(可以只盖到边的一部分)。
化边为点,支配树求必经边。dijkstra跑出最短路,问题转换成数轴上的覆盖,可以随便搞。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=400007;
typedef long long LL;
typedef double db;
using namespace std;
int Test,n,m,s,t,L;
int a[N],f[N],sum[N];template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}struct Tree {int fa[N],fg[N],sdom[N],idom[N];int find(int x) {if(fa[x]==x) return x;int nf=find(fa[x]);if(sdom[fg[fa[x]]]<sdom[fg[x]]) fg[x]=fg[fa[x]];fa[x]=nf; return nf;    }int qry(int x) {find(x);return fg[x];}vector<int>vc[N],eg[N];int ecnt,fir[N],nxt[N],to[N];void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;eg[v].push_back(u);}int p[N],dfk,tid[N],dfn[N];void dfs(int x,int Fa) {p[x]=Fa;dfn[x]=++dfk;tid[dfk]=x;sdom[x]=dfk;for(int i=fir[x];i;i=nxt[i]) if(!dfn[to[i]])dfs(to[i],x);}void build() {dfs(s,0);Rep(i,dfk,2) {int x=tid[i],F=p[x],up=eg[x].size();For(j,0,up-1) {int y=eg[x][j];if(dfn[y]) sdom[x]=min(sdom[x],sdom[qry(y)]);}vc[tid[sdom[x]]].push_back(x);fa[x]=F; up=vc[F].size();For(j,0,up-1) {int w=vc[F][j];int v=qry(w);idom[w]=(sdom[v]==sdom[w]?F:v);}}For(i,2,dfk) { int x=tid[i]; idom[x]=(idom[x]==tid[sdom[x]]?idom[x]:idom[idom[x]]); }For(i,2,dfk) { int x=tid[i]; sdom[x]=tid[sdom[x]]; }}void init() {ecnt=dfk=0;For(i,1,n+m) fir[i]=dfn[i]=sdom[i]=idom[i]=0,fa[i]=fg[i]=i,vc[i].clear(),eg[i].clear();}
}T;struct node {int x,dis;friend bool operator <(const node&A,const node&B) {return A.dis>B.dis;}
};
priority_queue<node>que;#define inf 1e9
struct Graph {int ecnt,fir[N],nxt[N],fr[N],to[N],val[N];void add(int u,int v,int w) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; fr[ecnt]=u; to[ecnt]=v; val[ecnt]=w;}int dis[N];void dijkstra() {For(i,1,n) dis[i]=inf;dis[s]=0;que.push((node){s,0});while(!que.empty()) {node tp=que.top();que.pop();int x=tp.x;if(dis[x]!=tp.dis) continue;for(int i=fir[x];i;i=nxt[i]) {int y=to[i];if(dis[y]>dis[x]+val[i]) {dis[y]=dis[x]+val[i];que.push((node){y,dis[y]});}}}}void init() {ecnt=0;memset(fir,0,sizeof(fir));}
}G;int main() {//freopen("3281.in","r",stdin);//freopen("3281.out","w",stdout);read(Test);while(Test--) {read(n); read(m); read(s); read(t); s++; t++ ;read(L);T.init(); G.init();For(i,1,m) {int u,v,w;read(u); read(v); read(w);u++; v++;G.add(u,v,w);T.add(u,n+i);T.add(n+i,v);}T.build();G.dijkstra();if(G.dis[t]==inf) puts("-1");else {a[0]=0; int x=t;while(x!=s) {x=T.idom[x];if(x>n) a[++a[0]]=x-n;}For(i,1,a[0]) if(i<a[0]-i+1) swap(a[i],a[a[0]-i+1]);int ans=0;For(i,1,a[0]) sum[i]=sum[i-1]+G.val[a[i]];For(i,1,a[0]) {f[i]=0;int l=1,r=i,rs=1,v=G.to[a[i]];while(l<=r) {int mid=((l+r)>>1);int u=G.to[a[mid]];if(G.dis[v]-G.dis[u]<L) rs=mid,r=mid-1;else l=mid+1;}int u=G.fr[a[rs]];f[i]=sum[i]-sum[rs-1];if(G.dis[v]-G.dis[u]>L) f[i]-=(G.dis[v]-G.dis[u]-L);ans=max(ans,f[rs-1]+f[i]);l=1,r=i,rs=1,v=G.to[a[i]];while(l<=r) {int mid=((l+r)>>1);int u=G.to[a[mid]];if(G.dis[v]-G.dis[u]<L*2) rs=mid,r=mid-1;else l=mid+1;}u=G.fr[a[rs]];int tp=sum[i]-sum[rs-1];if(G.dis[v]-G.dis[u]>L*2) tp-=(G.dis[v]-G.dis[u]-L*2);ans=max(ans,tp);f[i]=max(f[i],f[i-1]);}printf("%d\n",sum[a[0]]-ans);}}Formylove;
}

test2018.3.3:problem C

传送门
问1到n的最短路上的必经点。
怎么是支配树的裸题啊。跑最短路后把dis[u]+val(u,v)>dis[v]的边(u,v)删掉,求支配点即可。
还问为什么求割点不行,当年我真是傻得可爱。

//Achen
#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
#define Formylove return 0
const int N=400007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m;template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}struct Tree {int fa[N],fg[N],sdom[N],idom[N];int find(int x) {if(fa[x]==x) return x;int nf=find(fa[x]);if(sdom[fg[fa[x]]]<sdom[fg[x]]) fg[x]=fg[fa[x]];fa[x]=nf; return nf;    }int qry(int x) {find(x);return fg[x];}vector<int>vc[N],eg[N];int ecnt,fir[N],nxt[N],to[N];void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;eg[v].push_back(u);}int p[N],dfk,tid[N],dfn[N];void dfs(int x,int Fa) {p[x]=Fa;dfn[x]=++dfk;tid[dfk]=x;sdom[x]=dfk;for(int i=fir[x];i;i=nxt[i]) if(!dfn[to[i]])dfs(to[i],x);}void build() {dfs(1,0);Rep(i,dfk,2) {int x=tid[i],F=p[x],up=eg[x].size();For(j,0,up-1) {int y=eg[x][j];if(dfn[y]) sdom[x]=min(sdom[x],sdom[qry(y)]);}vc[tid[sdom[x]]].push_back(x);fa[x]=F; up=vc[F].size();For(j,0,up-1) {int w=vc[F][j];int v=qry(w);idom[w]=(sdom[v]==sdom[w]?F:v);}}For(i,2,dfk) { int x=tid[i]; idom[x]=(idom[x]==tid[sdom[x]]?idom[x]:idom[idom[x]]); }For(i,2,dfk) { int x=tid[i]; sdom[x]=tid[sdom[x]]; }}void init() {ecnt=dfk=0;For(i,1,n) fa[i]=fg[i]=i;}
}T;struct node {int x,dis;friend bool operator <(const node&A,const node&B) {return A.dis>B.dis;}
};
priority_queue<node>que;#define inf 1e9
struct Graph {int ecnt,fir[N],nxt[N],fr[N],to[N],val[N];void add(int u,int v,int w) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; fr[ecnt]=u; to[ecnt]=v; val[ecnt]=w;}int dis[N];void dijkstra(int s,int t) {For(i,1,n) dis[i]=inf;dis[s]=0;que.push((node){s,0});while(!que.empty()) {node tp=que.top();que.pop();int x=tp.x;if(dis[x]!=tp.dis) continue;for(int i=fir[x];i;i=nxt[i]) {int y=to[i];if(dis[y]>dis[x]+val[i]) {dis[y]=dis[x]+val[i];que.push((node){y,dis[y]});}}}}void solve() {dijkstra(1,n);if(dis[n]>=inf) {puts("-1");return ;}T.init();For(i,1,ecnt) if(dis[fr[i]]+val[i]==dis[to[i]])T.add(fr[i],to[i]);T.build();int ans=1;for(int x=n;x!=1;x=T.idom[x]) ans++;printf("%d\n",ans);}
}G;int main() {freopen("C.in","r",stdin);freopen("C.out","w",stdout);read(n); read(m);For(i,1,m) {int u,v,w;read(u); read(v); read(w);G.add(u,v,w);}G.solve();Formylove;
}

仙人掌和圆方树

如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人掌
图(cactus)。所谓简单回路就是指在图上不重复经过任何一个顶点的回路。

どこでもドア
仙人掌中每个环都是一个点双,给每个点双建一个方点代表这个点双,环上每一个点视为圆点,都向这个方点连边。特别的,两个点一条边也看成一个点双。圆点只会与圆点相连,方点只会与方点相连。

bzoj2125: 最短路

どこでもドア
给定仙人掌,多次询问两点间最短路。
建出圆方树,方点到父亲(方点的父亲视为这个环的代表点)的距离设为0,方点的儿子到方点的距离设为点到代表点的距离的最小值。每次询问求书上最短路并讨论lca是否是方点即可。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=20007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,q,tot;template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}map<LL,LL>mp;
LL get(int x,int y) { return 100000LL*min(x,y)+max(x,y); }struct Tree {LL R[N],H[N],val[N<<1],L[N],ML[N];int ecnt,fir[N],nxt[N<<1],to[N<<1];void add(int u,int v,LL w,LL ww) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w; ML[v]=ww;}int f[N][18];void dfs(int x,int fa) {f[x][0]=fa;R[x]=R[fa]+1;For(i,1,15) f[x][i]=f[f[x][i-1]][i-1];for(int i=fir[x];i;i=nxt[i]) {H[to[i]]=H[x]+val[i];dfs(to[i],x);}}LL get_dis(int x,int y) {if(x==y) return 0;if(R[x]<R[y]) swap(x,y);int a=x,b=y;Rep(i,15,0) if(R[f[x][i]]>=R[y])x=f[x][i];if(x==y) return H[a]-H[x];Rep(i,15,0) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];int z=f[x][0];if(f[x][0]<=n) return H[a]+H[b]-2LL*H[z];else return H[a]-H[x]+H[b]-H[y]+min(L[z]-abs(ML[x]-ML[y]),abs(ML[x]-ML[y]));}}T;struct Graph{int ecnt,fir[N],nxt[N<<1],to[N<<1];LL val[N<<1];void init()  { ecnt=1; tot=n; }void add(int u,int v,LL w) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w;}int dfn[N],low[N],sta[N],top,dfs_clock;void tarjan(int x,int F) {dfn[x]=low[x]=++dfs_clock;for(int i=fir[x];i;i=nxt[i]) if((i^1)!=F) {if(!dfn[to[i]]) {sta[++top]=i;tarjan(to[i],i);if(low[to[i]]>=dfn[x]) {T.add(x,++tot,0,0);LL H=mp[get(x,to[sta[top]])],L=H;for(int j=top;j;j--) {H+=val[sta[j]];if(sta[j]==i) break;}T.L[tot]=H;while(top) {int j=sta[top--];T.add(tot,to[j],min(L,H-L),L);L+=val[j];if(j==i) break;}}low[x]=min(low[x],low[to[i]]);}else low[x]=min(low[x],dfn[to[i]]);}}
}G;//#define DEBUG
int main() {#ifdef DEBUGfreopen("1.in","r",stdin);//freopen(".out","w",stdout);
#endifread(n); read(m); read(q);G.init();For(i,1,m) {int u,v; LL w;read(u); read(v); read(w);mp[get(u,v)]=w;G.add(u,v,w);}G.tarjan(1,0);T.dfs(1,0);For(i,1,q) {int x,y;read(x); read(y);printf("%lld\n",T.get_dis(x,y));}return 0;
}

bzoj4316: 小C的独立集

どこでもドア
给定仙人掌,求最大独立集。

建出圆方树然后随便怎么dp,我是用f[x][0/1]表示圆点自己选不选,g[x][0/1][0/1]表示方点下面最左边和最右边的圆点选不选的答案。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=2*60007;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,q,tot,ans,sum;template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}map<LL,LL>mp;
LL get(int x,int y) { return 100000LL*min(x,y)+max(x,y); }struct Tree {int ecnt,fir[N],nxt[N<<1],to[N<<1],vis[N];int g[N][2][2],f[N][2];void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;}void dfs(int x) {int tp[2][2][2],o=0;if(x>n) memset(tp,0,sizeof(tp));else f[x][1]=1; for(int i=fir[x];i;i=nxt[i]) {dfs(to[i]);if(x<=n) {int tp=0; For(j,0,1) For(k,0,1) tp=max(tp,g[to[i]][j][k]);f[x][0]+=tp;f[x][1]+=g[to[i]][0][0];}else {if(!vis[x]) {tp[o][0][0]=f[to[i]][0];tp[o][1][1]=max(f[to[i]][1],f[to[i]][0]); vis[x]=1;}else {o^=1;For(j,0,1) {tp[o][j][1]=tp[o^1][j][0]+max(f[to[i]][1],f[to[i]][0]);tp[o][j][0]=max(tp[o^1][j][0],tp[o^1][j][1])+f[to[i]][0];}}}}if(x>n) {For(j,0,1) For(k,0,1) { g[x][j][k]=tp[o][j][k]; ans=max(ans,tp[o][j][k]); }        }else For(i,0,1) ans=max(ans,f[x][i]);}
}T;struct Graph{int ecnt,fir[N],nxt[N<<1],to[N<<1];void init()  { ecnt=1; tot=n; }void add(int u,int v,LL w) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;}int dfn[N],low[N],sta[N],top,dfs_clock;void tarjan(int x,int F) {dfn[x]=low[x]=++dfs_clock;for(int i=fir[x];i;i=nxt[i]) if((i^1)!=F) {if(!dfn[to[i]]) {sta[++top]=i;tarjan(to[i],i);if(low[to[i]]>=dfn[x]) {T.add(x,++tot);while(top) {int j=sta[top--];T.add(tot,to[j]);if(j==i) break;}}low[x]=min(low[x],low[to[i]]);}else low[x]=min(low[x],dfn[to[i]]);}}
}G;//#define DEBUG
int main() {#ifdef DEBUGfreopen("1.in","r",stdin);//freopen(".out","w",stdout);
#endifread(n); read(m);G.init();For(i,1,m) {int u,v; LL w;read(u); read(v);G.add(u,v,w);}For(i,1,n) if(!G.dfn[i]) {G.dfs_clock=0;G.tarjan(1,0);ans=0; T.dfs(1); sum+=ans;}printf("%d\n",sum);return 0;
}

bzoj1023: [SHOI2008]cactus仙人掌图

どこでもドア
求仙人掌直径,圆方树上dp,单调队列优化。

//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<queue>
#include<cmath>
#include<set>
#include<map>
#define For(i,a,b) for(int i=(a);i<=(b);i++)
#define Rep(i,a,b) for(int i=(a);i>=(b);i--)
const int N=2e5+7;
typedef long long LL;
typedef double db;
using namespace std;
int n,m,tot,ans;template<typename T> void read(T &x) {char ch=getchar(); x=0; T f=1;while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') f=-1,ch=getchar();for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f;
}struct Tree {int ecnt,fir[N],nxt[N<<1],to[N<<1],que[N<<1],ql,qr,f[N],tp[N],H;void init() {ecnt=0;memset(fir,0,sizeof(fir));memset(f,0,sizeof(f));}void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;}void dfs(int x) {int fi=0,se=0;for(int i=fir[x];i;i=nxt[i]) dfs(to[i]);if(x<=n) {for(int i=fir[x];i;i=nxt[i]) {se=max(se,f[to[i]]);if(se>fi) swap(fi,se);}ans=max(ans,fi+se); f[x]=fi;}else {ql=1; qr=0; H=1;for(int i=fir[x];i;i=nxt[i])tp[++H]=to[i];For(i,1,H) {tp[i+H]=tp[i];f[x]=max(f[x],f[tp[i]]+min(i-1,H-(i-1)));}For(i,1,H+H/2) {if(ql<=qr&&i-que[ql]>H/2) ql++;if(ql<=qr) ans=max(ans,i-que[ql]+f[tp[que[ql]]]+f[tp[i]]);while(ql<=qr&&f[tp[que[qr]]]<=f[tp[i]]) qr--;que[++qr]=i;}}}
}T;struct Graph{int ecnt,fir[N],nxt[N<<1],to[N<<1];int dfn[N],low[N],sta[N],top,dfs_clock;void init()  { ecnt=1; tot=n; dfs_clock=0;memset(dfn,0,sizeof(dfn));memset(fir,0,sizeof(fir));}void add(int u,int v) {nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v;nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u;}void tarjan(int x,int F) {dfn[x]=low[x]=++dfs_clock;for(int i=fir[x];i;i=nxt[i]) if((i^1)!=F) {if(!dfn[to[i]]) {sta[++top]=i;tarjan(to[i],i);if(low[to[i]]>=dfn[x]) {T.add(x,++tot);while(top) {int j=sta[top--];T.add(tot,to[j]);if(j==i) break;}}low[x]=min(low[x],low[to[i]]);}else low[x]=min(low[x],dfn[to[i]]);}}
}G;//#define DEBUG
int main() {#ifdef DEBUGfreopen("1.in","r",stdin);//freopen(".out","w",stdout);
#endifwhile(scanf("%d%d",&n,&m)==2) {G.init(); T.init();For(i,1,m) {int k,pr,x; read(k); read(pr);For(i,1,k-1) {read(x); G.add(pr,x); pr=x;}}G.tarjan(1,0);T.dfs(1);printf("%d\n",ans);}return 0;
}

图论5:Tarjan!塔尖!相关推荐

  1. 图论之tarjan缩点

    缩点,就是把一张有向有环图中的环缩成一个个点,形成一个有向无环图. 首先我介绍一下为什么这题要缩点(有人肯定觉得这是放屁,这不就是缩点的模板题吗?但我们不能这么想,考试的时候不会有人告诉你打什么板上去 ...

  2. 【图论】Tarjan 缩点

    [Tarjan]缩点 在一个点N数据极大的图中,直接SPFA或者记忆花搜索时间超限,那么我们可以利用Tarjan缩点来减少N. 举个例子: 如上图:3,6两点为该图中的强连通分量,我们可以将它们看做一 ...

  3. 图论总结tarjan算法

    tarjan算法,是一个可以在有向图中找到强连通分量的的算法. 首先你要了解什么是强连通,以及什么是强连通分量. 下面是我给的简释: 一.强连通. 就是在一个有向图(记为G)中,如果两个点(记为a,b ...

  4. 图论之tarjan真乃神人也,强连通分量,割点,桥,双连通他都会

    先来%一下Robert Tarjan前辈 %%%%%%%%%%%%%%%%%% 然后是热情感谢下列并不止这些大佬的博客: 图连通性(一):Tarjan算法求解有向图强连通分量 图连通性(二):Tarj ...

  5. 『ACM』ACM模板合集

    写在前面: 第一年小白拿铜牌,第二年队友出走,加上疫情原因不能回校训练导致心底防线彻底崩盘,于是选择退役. 自从退役之后,一直想我打了那么久的ACM,什么也没留下觉得很难受,突然想到我打ACM的时候, ...

  6. NOIP提高组复赛 知识点整理

    枚举.模拟.贪心.递推.排序(快排) 高精度: 加法,减法,乘法(应该只会有高精乘单精),高精度除单精 分治: 二分查找 整体二分 CDQ分治 三分 搜索: dfs.剪枝 bfs.双向bfs 启发式搜 ...

  7. html标签属性可以省略,html部分---通用标签与属性;

    body的属性: bgcolor页面背景色:text文字颜色:topmargin上页边距:leftmargin左页边距:rightmargin右页边距:bottomargin下页边距: src是引用过 ...

  8. 蓝桥杯大赛决赛整理合集(B组C/C++)

    蓝桥杯大赛决赛整理合集(B组C/C++) 根据大纲梳理一遍,也在全文最后补充了最几年的决赛真题,全文基于C++编写,希望对你有所帮助 关于省赛的反思: 1.我的Code::Block 20.03在机房 ...

  9. 算法提高课-图论-有向图的强连通分量-AcWing 367. 学校网络:强连通分量、tarjan算法

    文章目录 题目解答 题目来源 题目解答 来源:acwing 分析: 第一问:通过tarjan算法求出强连通分量并且缩点后,统计入度为0的点的个数p即可. 第二问,至少加几条边才能使图变成强连通分量?这 ...

最新文章

  1. 并发控制--悲观锁和乐观锁详解
  2. SpringBoot新版
  3. SmartNews:基于 Flink 加速 Hive 日表生产的实践
  4. 解压的mysql_10分钟教你解决安装解压版mysql出现的各种问题
  5. 关于MPMoviePlayerController类播放视频时,外放没有声音的问题(ios)
  6. iOS开发之理解iOS中的MVC设计模式
  7. json对象转换为字符串数组 java_Json对象与Json字符串的转化、JSON字符串与Java对象的转换...
  8. 在线License管理系统(支持离线授权)
  9. python使用openpyxl插入excel批注,同时修改excel批注框大小
  10. 汉字编码与拼音输入法
  11. ( 方框打勾 java_Java 11手册:Java 11是否在所有正确的方框中打勾?
  12. Js 把html字符串显示,js Html结构转字符串形式显示代码
  13. 关于Windows无法访问指定设备路径或文件,您可能没有合适的权限访问问题解决转
  14. 列出对象属性,for(var i in obj)
  15. CLEval: Character-Level Evaluation for Text Detection and Recognition Task 论文阅读
  16. Java基础-基础知识汇总
  17. 数据结构二叉树之Huffman编码
  18. 英语基础不好可以学会编程吗?
  19. 计算机硬件故障检测实验报告,计算机系统的硬件检测实验报告
  20. 滴滴外卖无锡首战告捷,外卖大战的关键不在于技术?

热门文章

  1. Eigen入门系列 —— Eigen::Matrix矩阵点乘、叉乘、转置、求逆、求和、行列式、迹、数乘
  2. openldap2.4版本管理员文档中文翻译版
  3. 星座判断选择(GO语言)
  4. 条码标签制作软件打印入场券抽奖券上的条码流水号
  5. 美国访问学者博士后面签常见问题
  6. Async使用问题,引起的生产事故
  7. ASCII Art@字符画LOGO字符生成@Font Generator
  8. java毕业生设计蛋糕店会员系统的设计与实现计算机源码+系统+mysql+调试部署+lw
  9. 无人驾驶之概率机器人,附加部分,及强化学习试卷
  10. 用深度强化学习玩超级马里奥兄弟