題解 CF786B 【Legacy】
阿新 • • 發佈:2020-08-20
我表示這是老早以前的 \(XJ\) 練習題,但是我一直在咕咕咕,所以現在才做完。
題目大意
給你三種建圖方式:
- 點 \(u\) 向點 \(v\) 連一條邊,邊權為 \(w\) 。
- 點 \(u\) 向 \([l,r]\) 區間內的點連一條邊,邊權均為 \(w\)。
- \([l,r]\) 區間內的點向點 \(v\) 連一條邊,邊權均為 \(w\)。
最後詢問你一個點的單源最短路。
題解
我們可以比較輕易的想到,這是一道關於區間操作的題,我們需要一些關於區間操作的資料結構來優化連邊,因為直接連邊是肯定會炸的。
表示我們可以考慮線段樹。因為向一段區間連邊線上段樹上就可以表示為向 \(log\) 級別個數的節點連邊,那麼我們只要將這棵線段樹的父節點與子節點之間的連邊處理好,就可以利用線段樹的節點作為點的編號來進行建圖。
如圖,所以我們甚至不需要建線段樹,只需要線段樹的節點編號就可以了。
還有,這道題卡 \(spfa\) ,甚至多帶一個 \(log\) 都能被卡,傷心……
程式碼如下:
#include<bits/stdc++.h> using namespace std; #define int long long const int N=1e5+5; int n,m,s; int opt,u,v,l,r,w; struct Edge{int nxt,to,val;}e[N<<6]; int fir[N<<4],size=0; void add(int u,int v,int w) { e[++size]=Edge{fir[u],v,w}; fir[u]=size; return ; } void build(int p,int l,int r) { if(l==r) { add(p+(N<<2),p,0); return ; } add(p<<1,p,0),add(p<<1|1,p,0); add(p+(N<<2),(p<<1)+(N<<2),0); add(p+(N<<2),(p<<1|1)+(N<<2),0); int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); return ; } void find(int p,int l,int r,int x,int y,vector<int> &now) { if(x<=l&&r<=y) { now.push_back(p); return ; } int mid=(l+r)>>1; if(x<=mid) find(p<<1,l,mid,x,y,now); if(y>=mid+1) find(p<<1|1,mid+1,r,x,y,now); return ; } int dis[N<<4]; priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q; bool vis[N<<4]; void dijkstra() { for(int i=0;i<(N<<3);++i) dis[i]=1e18+7; vector<int> now;find(1,1,n,s,s,now); dis[now[0]]=0; q.push(make_pair(dis[now[0]],now[0])); while(!q.empty()) { while(!q.empty()&&vis[q.top().second]) q.pop(); if(q.empty()) break; pair<int,int> tmp=q.top();q.pop(),vis[tmp.second]=true; for(int i=fir[tmp.second];i;i=e[i].nxt) { if(dis[e[i].to]>tmp.first+e[i].val) { dis[e[i].to]=tmp.first+e[i].val; q.push(make_pair(dis[e[i].to],e[i].to)); } } } return ; } signed main() { cin>>n>>m>>s; build(1,1,n); for(int i=1;i<=m;++i) { scanf("%lld",&opt); if(opt==1) { scanf("%lld%lld%lld",&u,&v,&w); vector<int> a;find(1,1,n,u,u,a); vector<int> b;find(1,1,n,v,v,b); add(a[0],b[0]+(N<<2),w); } if(opt==2) { scanf("%lld%lld%lld%lld",&u,&l,&r,&w); vector<int> a;find(1,1,n,u,u,a); vector<int> b;find(1,1,n,l,r,b); for(int i=0;i<a.size();++i) { for(int j=0;j<b.size();++j) add(a[i],b[j]+(N<<2),w); } } if(opt==3) { scanf("%lld%lld%lld%lld",&v,&l,&r,&w); vector<int> a;find(1,1,n,l,r,a); vector<int> b;find(1,1,n,v,v,b); for(int i=0;i<a.size();++i) { for(int j=0;j<b.size();++j) add(a[i],b[j]+(N<<2),w); } } } dijkstra(); for(int i=1;i<=n;++i) { vector<int> now;find(1,1,n,i,i,now); if(dis[now[0]]==1e18+7) printf("-1 "); else printf("%lld ",dis[now[0]]); } printf("\n"); return 0; }