淺談建圖優化 - 線段樹優化
阿新 • • 發佈:2020-10-18
更新記錄
【1】2020.10.16-20:40
- 1.完善內容
正文
在一些圖論題中,我們可能會遇到一些題目讓你對區間進行連邊
這個時候如果我們暴力進行連邊顯然是會超時的
我們就要考慮有沒有什麼辦法可以快速的進行區間連邊
這個時候就要祭出區間操作的神器 - 線段樹
線段樹上的非葉節點表示的是區間,我們就可以直接對他們進行連邊
舉個例子,我們連一條 \(2\) 與區間 \([3,5]\) 之間的雙向邊
首先進行建樹:
我們發現,如果只建一棵樹的話,會出現訪問錯誤
例如 \([mid+1,r]\) 會沿著區間錯誤的訪問到 \([l,r]\)
所以我們正反邊各建一棵線段樹:
然後讓葉節點沒有入度的那棵線段樹向節點 \(2\)
就像這樣:
之後我們按照原來的套路進行操作即可
CF786B Legacy
【題意簡化】
給你一個 \(n\) 個點的有向圖,圖上有 \(3\) 種邊
- 1.\(u\to v\) 邊權為 \(w\)
- 2.\(u\to[l,r]\) 邊權為 \(w\)
- 3.\([l,r]\to u\) 邊權為 \(w\)
求 \(s\) 點到所有點的最短路
這道題還算是有些麻煩,對於第一種邊直接連線即可
第二種第三種就是將上面講的雙向邊的連線拆成單向邊進行連線
【程式碼實現】
#include<iostream> #include<queue> #include<cstring> #define int long long #define N 900004 #define M 5000050 using namespace std; int n,q,s,num,head[N],dis[N],n2,n4,n8; bool vis[N]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >que; namespace in {int type,l,r,f,t,w;} struct Edge {int na,np,w;} e[M]; struct segtree {int l,r;} t[N]; inline void add(int f,int t,int w=0){ e[++num].na=head[f]; e[num].np=t;e[num].w=w; head[f]=num; } inline void dbadd(int f,int t,int w=0) {add(f,t,w);add(t,f,w);} inline void tadd(int p,int plus=0,bool s=0) { if(!s) add(p+plus,(p<<1)+plus),add(p+plus,(p<<1|1)+plus); else add((p<<1)+plus,p+plus),add((p<<1|1)+plus,p+plus); } inline void build(int p,int l,int r){ t[p].l=l;t[p].r=r; if(l==r){ dbadd(l+n8,p); dbadd(l+n8,p+n4); return; } tadd(p);tadd(p,n4,1); int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } inline void secadd(int p,int l,int r,int wp,int w,bool s){ if(t[p].l==l&&t[p].r==r){ if(!s) add(wp+n8,p,w);//s to p else add(p+n4,wp+n8,w); return; } int mid=(t[p].l+t[p].r)>>1; if(l<=mid) secadd(p<<1,l,r<mid?r:mid,wp,w,s); if(r>mid) secadd(p<<1|1,mid+1<l?l:mid+1,r,wp,w,s); } inline void dij(){ memset(dis,0x7f,sizeof(dis)); dis[s]=0;que.push(make_pair(0,s)); while(!que.empty()){ int p=que.top().second;que.pop(); if(vis[p]) continue; vis[p]=1; for(int i=head[p];i;i=e[i].na){ if(dis[e[i].np]>dis[p]+e[i].w){ dis[e[i].np]=dis[p]+e[i].w; que.push(make_pair(dis[e[i].np],e[i].np)); } } } } signed main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>n>>q>>s; n2=n<<1;n4=n<<2;n8=n<<3; build(1,1,n); using namespace in; for(int i=1;i<=q;i++){ cin>>type; if(type==1){ cin>>f>>in::t>>w; add(f+n8,in::t+n8,w); } else if(type==2){ cin>>f>>l>>r>>w; secadd(1,l,r,f,w,0); } else{ cin>>in::t>>l>>r>>w; secadd(1,l,r,in::t,w,1); } } s+=n8; dij(); for(int i=1,p=n8;i<=n;i++) cout<<(dis[i+p]<1e18?dis[i+p]:-1)<<" \n"[i==n]; }
P6348 [PA2011]Journeys
這道題就是讓我們區間對區間進行連邊,然後求最短路
我們可以建兩個虛點,然後這兩個虛點之間的邊的權值為 \(1\),其他的邊均為 \(0\)
第一個區間連到第一個點上,第二個區間連到第二個點上
因為是雙向邊,我們連兩次單項邊即可
又因為這道題只有 \(0\) 與 \(1\) 兩個權值,我們可以不用最短路,而直接用01BFS去求
【程式碼實現】
#include<iostream> #include<cstring> #include<queue> #define N 10000006 using namespace std; int n,m,p,fl,fr,tl,tr,n2,n4,n8,dis[N],head[N],num,nont[N],cnt; struct Edge{int na,np,w;} e[N]; struct segtree{int l,r;} t[N]; deque<int>que; inline void add(int f,int t,int w=0){ e[++num].na=head[f]; e[num].np=t;e[num].w=w; head[f]=num; } inline void dbadd(int f,int t) {add(f,t);add(t,f);} inline void build(int p,int l,int r){ t[p].l=l;t[p].r=r; if(l==r){ nont[l]=p; dbadd(p,p+n4); return; } add(p,p<<1),add(p,p<<1|1); add((p<<1)+n4,p+n4),add((p<<1|1)+n4,p+n4); int mid=(l+r)>>1; build(p<<1,l,mid);build(p<<1|1,mid+1,r); } inline void sadd(int p,int l,int r,int wp,bool s){ if(t[p].l==l&&t[p].r==r){ if(!s) add(wp,p); else add(p+n4,wp); return; } int mid=(t[p].l+t[p].r)>>1; if(l<=mid) sadd(p<<1,l,r<mid?r:mid,wp,s); if(r>mid) sadd(p<<1|1,mid+1<l?l:mid+1,r,wp,s); } inline void dbsadd(int l1,int r1,int l2,int r2){ int p1=++cnt,p2=++cnt; add(p2,p1,1); sadd(1,l2,r2,p1,0); sadd(1,l1,r1,p2,1); p1=++cnt,p2=++cnt; add(p2,p1,1); sadd(1,l2,r2,p2,1); sadd(1,l1,r1,p1,0); } inline void dij(){ memset(dis,0x3f,sizeof(dis)); dis[p]=0;que.push_front(p); while(que.size()){ int bf=que.front();que.pop_front(); for(int i=head[bf];i;i=e[i].na){ if(dis[e[i].np]>dis[bf]+e[i].w){ dis[e[i].np]=dis[bf]+e[i].w; if(!e[i].w) que.push_front(e[i].np); else que.push_back(e[i].np); } } } } signed main(){ cin>>n>>m>>p; n2=n<<1;n4=n<<2;n8=n<<3;cnt=n8; build(1,1,n); for(int i=1;i<=m;i++){ cin>>fl>>fr>>tl>>tr; dbsadd(fl,fr,tl,tr); } p=nont[p]+n4; dij(); for(int i=1;i<=n;i++) cout<<dis[nont[i]]<<"\n"; }