文章目录
小模板
强联通分量
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 DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(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 DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
read(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 DEBUG
freopen("1.in","r",stdin);
//freopen(".out","w",stdout);
#endif
while(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;
}