常见优化建图技巧
一、线段树优化建图
基本操作:
向区间 连边 - 区间
向 连边 - 区间
向 区间 连边
建立两棵线段树,一棵从父亲节点向儿子节点连长度为
对于操作1,在入树中找到
对于操作2,在入树中找到
对于操作3,在入树中找到
这样,我们将边数规模从
CF786B Legacy & P6348 [PA2011] Journeys
板子题
code
// CF786B
#include<bits/stdc++.h>
#define pii pair<int,int>
#define pli pair<long long,int>
#define LL long long
using namespace std;
const int N=100010;
int n,m,s;
vector <pii> g[9*N];
void add(int x,int y,int z)
{
g[x].push_back({y,z});
}
namespace TR
{
#define lc(p) p*2
#define rc(p) p*2+1
int cnt;
struct SegmentTree
{
int id[4*N],pos[N];
void build(int p,int l,int r,int flag)
{
id[p]=++cnt;
if(l==r)
{
pos[l]=cnt;
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid,flag); build(rc(p),mid+1,r,flag);
if(!flag)
add(id[p],id[lc(p)],0),add(id[p],id[rc(p)],0);
else
add(id[lc(p)],id[p],0),add(id[rc(p)],id[p],0);
}
void addt(int p,int l,int r,int ql,int qr,int u,int w,int flag)
{
if(ql<=l && qr>=r)
{
if(!flag)
add(u,id[p],w);
else
add(id[p],u,w);
return;
}
int mid=(l+r)>>1;
if(ql<=mid)
addt(lc(p),l,mid,ql,qr,u,w,flag);
if(qr>mid)
addt(rc(p),mid+1,r,ql,qr,u,w,flag);
}
}t[2];
}
namespace GR
{
LL d[9*N]; bool v[9*N];
priority_queue <pli> q;
void dijkstra()
{
memset(d,0x3f,sizeof(d));
d[TR::t[1].pos[s]]=0; q.push({0,TR::t[1].pos[s]});
while(q.size())
{
int x=q.top().second; q.pop();
if(v[x])
continue;
v[x]=1;
for(auto vv:g[x])
{
int y=vv.first,z=vv.second;
if(d[y]>d[x]+(LL)z)
d[y]=d[x]+(LL)z,q.push({-d[y],y});
}
}
}
}
int main()
{
using namespace TR;
using namespace GR;
scanf("%d%d%d",&n,&m,&s);
t[0].build(1,1,n,0); t[1].build(1,1,n,1);
for(int i=1; i<=n; i++)
add(t[0].pos[i],t[1].pos[i],0),add(t[1].pos[i],t[0].pos[i],0);
while(m--)
{
int op,u,v,w,l,r;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d",&u,&v,&w);
add(t[1].pos[u],t[0].pos[v],w);
}
else if(op==2)
{
scanf("%d%d%d%d",&u,&l,&r,&w);
t[0].addt(1,1,n,l,r,t[1].pos[u],w,0);
}
else
{
scanf("%d%d%d%d",&u,&l,&r,&w);
t[1].addt(1,1,n,l,r,t[0].pos[u],w,1);
}
}
dijkstra();
for(int i=1; i<=n; i++)
{
LL res=d[t[0].pos[i]];
if(res>=1e18)
printf("-1 ");
else
printf("%lld ",res);
}
return 0;
}
code
//P6348
#include<bits/stdc++.h>
#define pii pair<int,int>
#define pli pair<long long,int>
#define LL long long
using namespace std;
const int N=500010;
int n,m,s;
vector <pii> g[9*N];
void add(int x,int y,int z)
{
g[x].push_back({y,z});
}
namespace TR
{
#define lc(p) p*2
#define rc(p) p*2+1
int cnt;
struct SegmentTree
{
int id[4*N],pos[N];
void build(int p,int l,int r,int flag)
{
id[p]=++cnt;
if(l==r)
{
pos[l]=cnt;
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid,flag); build(rc(p),mid+1,r,flag);
if(!flag)
add(id[p],id[lc(p)],0),add(id[p],id[rc(p)],0);
else
add(id[lc(p)],id[p],0),add(id[rc(p)],id[p],0);
}
void addt(int p,int l,int r,int ql,int qr,int u,int w,int flag)
{
if(ql<=l && qr>=r)
{
if(!flag)
add(u,id[p],w);
else
add(id[p],u,w);
return;
}
int mid=(l+r)>>1;
if(ql<=mid)
addt(lc(p),l,mid,ql,qr,u,w,flag);
if(qr>mid)
addt(rc(p),mid+1,r,ql,qr,u,w,flag);
}
}t[2];
}
namespace GR
{
LL d[9*N]; bool v[9*N];
priority_queue <pli> q;
void dijkstra()
{
memset(d,0x3f,sizeof(d));
d[TR::t[1].pos[s]]=0; q.push({0,TR::t[1].pos[s]});
while(q.size())
{
int x=q.top().second; q.pop();
if(v[x])
continue;
v[x]=1;
for(auto vv:g[x])
{
int y=vv.first,z=vv.second;
if(d[y]>d[x]+(LL)z)
d[y]=d[x]+(LL)z,q.push({-d[y],y});
}
}
}
}
int main()
{
using namespace TR;
using namespace GR;
scanf("%d%d%d",&n,&m,&s);
t[0].build(1,1,n,0); t[1].build(1,1,n,1);
for(int i=1; i<=n; i++)
add(t[0].pos[i],t[1].pos[i],0),add(t[1].pos[i],t[0].pos[i],0);
while(m--)
{
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
t[1].addt(1,1,n,a,b,++cnt,1,1);
t[0].addt(1,1,n,c,d,cnt,0,0);
t[1].addt(1,1,n,c,d,++cnt,1,1);
t[0].addt(1,1,n,a,b,cnt,0,0);
}
dijkstra();
for(int i=1; i<=n; i++)
printf("%lld\n",d[t[0].pos[i]]);
return 0;
}
P5025 [SNOI2017] 炸弹
线段树优化建图+
缩点后是个DAG,要求每个点能够到达的所有点的权值总和。直接
因为点燃一个炸弹后他能引燃的一定是段区间,所以每个点
code
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N=500010;
const LL MOD=1e9+7;
int n;
LL ans,P[N],R_[N];
vector <int> g[9*N];
void add(int x,int y)
{
g[x].push_back(y);
}
namespace TR
{
#define lc(p) p*2
#define rc(p) p*2+1
int cnt,fid[9*N];
struct SegmentTree
{
int id[4*N],pos[N];
void build(int p,int l,int r,int flag)
{
id[p]=++cnt;
if(l==r)
{
pos[l]=cnt; fid[cnt]=l;
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid,flag); build(rc(p),mid+1,r,flag);
if(!flag)
add(id[p],id[lc(p)]),add(id[p],id[rc(p)]);
else
add(id[lc(p)],id[p]),add(id[rc(p)],id[p]);
}
void addt(int p,int l,int r,int ql,int qr,int u,int flag)
{
if(ql<=l && qr>=r)
{
if(!flag)
add(u,id[p]);
else
add(id[p],u);
return;
}
int mid=(l+r)>>1;
if(ql<=mid)
addt(lc(p),l,mid,ql,qr,u,flag);
if(qr>mid)
addt(rc(p),mid+1,r,ql,qr,u,flag);
}
}t[2];
}
namespace GR
{
const int NN=9*N;
int dfn[NN],low[NN],num;
int sta[NN],top,ins[NN],c[NN],tot;
int lo[NN],hi[NN];
bool vis[NN];
vector <int> gc[NN];
void tarjan(int x)
{
dfn[x]=low[x]=++num;
sta[++top]=x; ins[x]=1;
for(auto y:g[x])
{
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y])
low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
tot++; lo[tot]=1e9;
int y;
do
{
y=sta[top--]; ins[y]=0;
c[y]=tot;
if(TR::fid[y])
lo[tot]=min(lo[tot],TR::fid[y]),hi[tot]=max(hi[tot],TR::fid[y]);
}while(x!=y);
}
}
void addc(int x,int y)
{
gc[x].push_back(y);
}
void dfs(int x)
{
vis[x]=1;
for(auto y:gc[x])
{
if(!vis[y])
dfs(y);
lo[x]=min(lo[x],lo[y]);
hi[x]=max(hi[x],hi[y]);
}
return;
}
}
int main()
{
using namespace TR;
using namespace GR;
scanf("%d",&n);
for(int i=1; i<=n; i++)
scanf("%lld%lld",&P[i],&R_[i]);
t[0].build(1,1,n,0); t[1].build(1,1,n,1);
for(int i=1; i<=n; i++)
add(t[0].pos[i],t[1].pos[i]),add(t[1].pos[i],t[0].pos[i]);
P[n+1]=4e18;
for(int i=1; i<=n; i++)
{
int l=lower_bound(P+1,P+1+n,P[i]-R_[i])-P;
int r=upper_bound(P+1,P+1+n,P[i]+R_[i])-P-1;
t[0].addt(1,1,n,l,r,t[1].pos[i],0);
}
for(int i=1; i<=cnt; i++)
if(!dfn[i])
tarjan(i);
for(int i=1; i<=cnt; i++)
for(auto j:g[i])
if(c[i]!=c[j])
addc(c[i],c[j]);
for(int i=1; i<=tot; i++)
if(!vis[i])
dfs(i);
for(int i=1; i<=n; i++)
{
int cur=c[t[1].pos[i]];
(ans+=1LL*i*(hi[cur]-lo[cur]+1))%=MOD;
}
printf("%lld",ans);
return 0;
}
P3588 [POI2015] PUS
输入的
code
#include<bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
using namespace std;
const int N=100010;
int n,s,m,a[N],P[N],deg[5*N];
vector <pii> g[5*N];
void NIE()
{
puts("NIE");
exit(0);
}
void add(int x,int y,int z)
{
g[x].push_back({y,z});
deg[y]++;
}
namespace TR
{
#define lc(p) p*2
#define rc(p) p*2+1
int cnt,fid[5*N];
struct SegmentTree
{
int id[4*N],pos[N];
void build(int p,int l,int r)
{
id[p]=++cnt;
if(l==r)
{
pos[l]=cnt; fid[cnt]=l;
return;
}
int mid=(l+r)>>1;
build(lc(p),l,mid); build(rc(p),mid+1,r);
add(id[lc(p)],id[p],0); add(id[rc(p)],id[p],0);
}
void addt(int p,int l,int r,int ql,int qr,int u)
{
if(ql<=l && qr>=r)
{
add(id[p],u,0);
return;
}
int mid=(l+r)>>1;
if(ql<=mid)
addt(lc(p),l,mid,ql,qr,u);
if(qr>mid)
addt(rc(p),mid+1,r,ql,qr,u);
}
}t;
}
namespace GR
{
int val[5*N];
void topsort()
{
queue <int> q;
for(int i=1; i<=TR::cnt; i++)
{
if(!deg[i])
q.push(i),val[i]=1;
}
while(q.size())
{
int x=q.front(); q.pop(); //cout<<x<<" "<<val[x]<<endl;
if(TR::fid[x] && a[TR::fid[x]])
{
int v=TR::fid[x]; //cout<<"kk"<<v<<endl;
if(val[x]<=a[v])
val[x]=a[v];
else
NIE();
}
if(val[x]>1e9)
NIE();
for(auto v:g[x])
{
int y=v.first,z=v.second;
deg[y]--;
val[y]=max(val[y],val[x]+z);
if(!deg[y])
q.push(y);
}
}
for(int i=1; i<=TR::cnt; i++)
if(deg[i])
NIE();
}
}
int main()
{
using namespace TR;
using namespace GR;
scanf("%d%d%d",&n,&s,&m);
for(int i=1; i<=s; i++)
{
int x;
scanf("%d",&x);
scanf("%d",&a[x]);
}
t.build(1,1,n);
for(int i=1; i<=m; i++)
{
int k,l,r,lur,cur;
scanf("%d%d%d",&l,&r,&k);
P[0]=l-1; cnt++;
for(int j=1; j<=k; j++)
{
scanf("%d",&P[j]);
if(P[j-1]+1<=P[j]-1)
t.addt(1,1,n,P[j-1]+1,P[j]-1,cnt);
}
if(P[k]+1<=r)
t.addt(1,1,n,P[k]+1,r,cnt);
for(int j=1; j<=k; j++)
add(cnt,t.pos[P[j]],1);
}
topsort();
puts("TAK");
for(int i=1; i<=n; i++)
printf("%d ",val[t.pos[i]]);
return 0;
}
二、倍增 / 表优化建图
P5344 【XR-1】逛森林
倍增优化建图,每个点向上的
这样点数是
code
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
const int N=50010,M=1000010,NN=4000010,MM=24000010;
int n,m,s,t,cnt,id;
int f[N][20],dep[N],in[N][20],out[N][20],d[NN];
struct node{int u1,v1,u2,v2,w;}e[M];
int head[NN],ver[MM],edge[MM],nxt[MM],tot;
bool v[NN],vis[NN];
vector <int> g[N];
void add(int x,int y,int z)
{
ver[++tot]=y; edge[tot]=z;
nxt[tot]=head[x]; head[x]=tot;
}
struct DS
{
int fa[N];
void init()
{
for(int i=1; i<=n; i++)
fa[i]=i;
}
int get(int x)
{
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
}ds;
void dfs(int x,int fa)
{
dep[x]=dep[f[x][0]=fa]+1;
in[x][0]=++id; add(id,x,0); add(id,fa,0);
out[x][0]=++id; add(x,id,0); add(fa,id,0);
for(int i=1; i<=t; i++)
{
f[x][i]=f[f[x][i-1]][i-1];
in[x][i]=++id; add(id,in[x][i-1],0); add(id,in[f[x][i-1]][i-1],0);
out[x][i]=++id; add(out[x][i-1],id,0); add(out[f[x][i-1]][i-1],id,0);
}
for(int i=0; i<g[x].size(); i++)
{
int y=g[x][i];
if(y==fa)
continue;
dfs(y,x);
}
}
void LCA1(int x,int y,int k)
{
if(dep[x]<dep[y])
swap(x,y);
add(y,k,0);
for(int i=t; i>=0; i--)
if(dep[f[x][i]]>=dep[y])
add(out[x][i],k,0),x=f[x][i];
if(x==y)
return;
for(int i=t; i>=0; i--)
if(f[x][i]!=f[y][i])
add(out[x][i],k,0),add(out[y][i],k,0),x=f[x][i],y=f[y][i];
add(out[x][0],k,0);
return;
}
void LCA2(int x,int y,int k)
{
if(dep[x]<dep[y])
swap(x,y);
add(k,y,0);
for(int i=t; i>=0; i--)
if(dep[f[x][i]]>=dep[y])
add(k,in[x][i],0),x=f[x][i];
if(x==y)
return;
for(int i=t; i>=0; i--)
if(f[x][i]!=f[y][i])
add(k,in[x][i],0),add(k,in[y][i],0),x=f[x][i],y=f[y][i];
add(k,in[x][0],0);
}
void dijkstra()
{
memset(d,0x3f,sizeof(d));
priority_queue < pair<int,int> > q;
q.push(mp(0,s)); d[s]=0;
while(q.size())
{
int x=q.top().second; q.pop();
if(v[x])
continue;
v[x]=1;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
q.push(mp(-d[y],y));
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
ds.init(); id=n;
for(int i=1; i<=m; i++)
{
int op,x,y,z,u1,v1,u2,v2,w;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d%d%d",&u1,&v1,&u2,&v2,&w);
if(ds.get(u1)!=ds.get(v1) || ds.get(u2)!=ds.get(v2))
continue;
e[++cnt]=(node){u1,v1,u2,v2,w};
}
else
{
scanf("%d%d%d",&x,&y,&z);
int fx=ds.get(x),fy=ds.get(y);
if(fx==fy)
continue;
g[x].push_back(y); g[y].push_back(x);
add(x,y,z); add(y,x,z); ds.fa[fx]=fy;
}
}
t=log(n)/log(2);
for(int i=1; i<=n; i++)
if(ds.fa[i]==i)
dfs(i,0);
for(int i=1; i<=cnt; i++)
{
LCA1(e[i].u1,e[i].v1,++id);
LCA2(e[i].u2,e[i].v2,++id);
add(id-1,id,e[i].w);
}
dijkstra();
for(int i=1; i<=n; i++)
printf("%d ",d[i]>=1e9? -1:d[i]);
return 0;
}
考虑到题目只要求跑最短路,所以可以像
code
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
const int N=50010,M=1000010,NN=4000010,MM=20000010;
int n,m,s,t,cnt,id;
int f[N][20],dep[N],in[N][20],out[N][20],d[NN];
struct node{int u1,v1,u2,v2,w;}e[M];
int head[NN],ver[MM],edge[MM],nxt[MM],tot;
bool v[NN],vis[NN];
vector <int> g[N];
void add(int x,int y,int z)
{
ver[++tot]=y; edge[tot]=z;
nxt[tot]=head[x]; head[x]=tot;
}
struct DS
{
int fa[N];
void init()
{
for(int i=1; i<=n; i++)
fa[i]=i;
}
int get(int x)
{
if(x==fa[x])
return x;
return fa[x]=get(fa[x]);
}
}ds;
void dfs(int x,int fa)
{
dep[x]=dep[f[x][0]=fa]+1;
in[x][0]=++id; add(id,x,0); add(id,fa,0);
out[x][0]=++id; add(x,id,0); add(fa,id,0);
for(int i=1; i<=t; i++)
{
f[x][i]=f[f[x][i-1]][i-1];
in[x][i]=++id; add(id,in[x][i-1],0); add(id,in[f[x][i-1]][i-1],0);
out[x][i]=++id; add(out[x][i-1],id,0); add(out[f[x][i-1]][i-1],id,0);
}
for(int i=0; i<g[x].size(); i++)
{
int y=g[x][i];
if(y==fa)
continue;
dfs(y,x);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
for(int i=t; i>=0; i--)
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y)
return x;
for(int i=t; i>=0; i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
void link1(int x,int lca,int k)
{
if(x==lca)
{
add(x,k,0);
return;
}
int tt=log(dep[x]-dep[lca])/log(2);
add(out[x][tt],k,0);
for(int i=tt; i>=0; i--)
if(dep[f[x][i]]-(1<<tt)>=dep[lca])
x=f[x][i];
add(out[x][tt],k,0);
}
void link2(int x,int lca,int k)
{
if(x==lca)
{
add(k,x,0);
return;
}
int tt=log(dep[x]-dep[lca])/log(2);
add(k,in[x][tt],0);
for(int i=tt; i>=0; i--)
if(dep[f[x][i]]-(1<<tt)>=dep[lca])
x=f[x][i];
add(k,in[x][tt],0);
}
void dijkstra()
{
memset(d,0x3f,sizeof(d));
priority_queue < pair<int,int> > q;
q.push(mp(0,s)); d[s]=0;
while(q.size())
{
int x=q.top().second; q.pop();
if(v[x])
continue;
v[x]=1;
for(int i=head[x]; i; i=nxt[i])
{
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
q.push(mp(-d[y],y));
}
}
}
}
int main()
{
scanf("%d%d%d",&n,&m,&s);
ds.init(); id=n;
for(int i=1; i<=m; i++)
{
int op,x,y,z,u1,v1,u2,v2,w;
scanf("%d",&op);
if(op==1)
{
scanf("%d%d%d%d%d",&u1,&v1,&u2,&v2,&w);
if(ds.get(u1)!=ds.get(v1) || ds.get(u2)!=ds.get(v2))
continue;
e[++cnt]=(node){u1,v1,u2,v2,w};
}
else
{
scanf("%d%d%d",&x,&y,&z);
int fx=ds.get(x),fy=ds.get(y);
if(fx==fy)
continue;
g[x].push_back(y); g[y].push_back(x);
add(x,y,z); add(y,x,z); ds.fa[fx]=fy;
}
}
t=log(n)/log(2)+1;
for(int i=1; i<=n; i++)
if(ds.fa[i]==i)
dfs(i,0);
for(int i=1; i<=cnt; i++)
{
int lca=LCA(e[i].u1,e[i].v1);
link1(e[i].u1,lca,++id); link1(e[i].v1,lca,id);
lca=LCA(e[i].u2,e[i].v2);
link2(e[i].u2,lca,++id); link2(e[i].v2,lca,id);
add(id-1,id,e[i].w);
}
dijkstra();
for(int i=1; i<=n; i++)
printf("%d ",d[i]>=1e9? -1:d[i]);
return 0;
}
三、其它技巧
P6822 [PA2012] Tax
可以发现每个点的贡献都和与它连接的边有关,尝试将贡献全部归到入边
考虑边转点,将一条边拆成
对于一个点,设与它连接的边为
但这样建边是
code
#include<bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define pli pair<LL,int>
using namespace std;
const int N=100010,M=200010;
int n,m;
struct node{int y,z,id;};vector <node> e[M];
vector <pii> g[2*M];
LL d[2*M];
bool v[2*M];
void dijkstra()
{
memset(d,0x3f,sizeof(d));
priority_queue <pli> q;
q.push({0,0}); d[0]=0;
while(q.size())
{
int x=q.top().second; q.pop();
if(v[x])
continue;
v[x]=1;
for(auto v:g[x])
{
int y=v.first,z=v.second;
if(d[y]>d[x]+z)
{
d[y]=d[x]+z;
q.push({-d[y],y});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1; i<=m; i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
e[x].push_back({y,z,i});
e[y].push_back({x,z,i+m});
g[i].push_back({i+m,z});
g[i+m].push_back({i,z});
}
for(auto v:e[1])
g[0].push_back({v.id,v.z});
for(int i=2; i<=n; i++)
{
sort(e[i].begin(),e[i].end(),[&](node x,node y){return x.z<y.z;});
int len=e[i].size();
for(int j=0; j<len-1; j++)
{
node u=e[i][j],v=e[i][j+1];
g[u.id].push_back({v.id,v.z-u.z});
g[v.id].push_back({u.id,0});
}
}
dijkstra();
LL ans=1e18;
for(auto v:e[n])
ans=min(ans,d[v.id]);
printf("%lld",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】