[ZJOI2016]旅行者 題解
阿新 • • 發佈:2022-03-31
整體二分+最短路
Statement
網格圖,有邊權,多次詢問兩點距離
\(n\times m\le 2\times 10^4,q\le 10^5,w\le 10^4\)
[ZJOI2016]旅行者 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
Solution
容易處理只有一個詢問的情況。多個詢問,網格圖考慮分治
對於當前矩形,將長邊切開,對於切開的這條線上的所有點跑單源最短路然後貢獻答案
對於在同一邊的詢問,遞迴即可,不在同一邊的則答案已知
容易發現這樣的複雜度是 \(T(n)=2T(n/2)+\sqrt n\log n =O(n\sqrt n \log n)\) ,此處 \(n\)
有點卡常,有一個優化是每次跑最短路的時候把初始距離設為 \(dis[x]+dis[st]\) ,\(x\) 為當前點,\(st\) 為當前起點
時間複雜度具體證明:複雜度
Code
#include<bits/stdc++.h> #define id(x,y) ((x-1)*m+y) // #define int register int using namespace std; const int N = 2e4+5; const int M = 2e5+5; char buf[1<<23],*p1=buf,*p2=buf,obuf[1<<23],*O=obuf; #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) int read(){ int s=0,w=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();} while(isdigit(ch))s=s*10+(ch^48),ch=getchar(); return s*w; } void print(long long x) { if(x>9) print(x/10); *O++=x%10+'0'; } bool cmin(int &a,int b){return a>b?a=b,1:0;} bool cmax(int &a,int b){return a<b?a=b,1:0;} struct Edge{int nex,to,dis;}edge[N<<3]; struct Item{int a,b,c,d,pos,ans;}qs[M],tl[M],tr[M],t[M]; int head[N],dis[N],q[(int)2e7+5]; bool vis[N],fg[N]; int n,m,Q,elen=1; void addedge(int u,int v,int w){ edge[++elen]=(Edge){head[u],v,w},head[u]=elen; edge[++elen]=(Edge){head[v],u,w},head[v]=elen; } void spfa(int l1,int r1,int l2,int r2,int st){ for(int i=l1;i<=r1;++i) for(int j=l2;j<=r2;vis[id(i,j)]=0,++j) if(!dis[st])dis[id(i,j)]=0x3fffffff; else dis[id(i,j)]+=dis[st]; int l=0,r=0; dis[st]=0,q[r++]=st; while(l<r){ int u=q[l++]; vis[u]=0; for(int e=head[u],v;v=edge[e].to,e;e=edge[e].nex) if(fg[v]&&dis[v]>dis[u]+edge[e].dis){ dis[v]=dis[u]+edge[e].dis; if(!vis[v])vis[v]=1,q[r++]=v; } } } void solve(int l1,int r1,int l2,int r2,int l,int r){ // printf("%d %d %d %d %d %d\n",l1,r1,l2,r2,l,r); if(l>r||l1==r1&&l2==r2)return ; if(r1-l1>r2-l2){ int mid=(l1+r1)/2; for(int i=l1;i<=r1;++i) for(int j=l2;j<=r2;++j)fg[id(i,j)]=1; for(int i=l2;i<=r2;++i){ spfa(l1,r1,l2,r2,id(mid,i)); for(int j=l;j<=r;++j) cmin(qs[j].ans,dis[id(qs[j].a,qs[j].b)]+dis[id(qs[j].c,qs[j].d)]); } for(int i=l1;i<=r1;++i) for(int j=l2;j<=r2;++j)fg[id(i,j)]=dis[id(i,j)]=0; int c1=0,c2=0,c3=0; for(int i=l;i<=r;++i) if(qs[i].a<=mid&&qs[i].c<=mid)tl[++c1]=qs[i]; else if(qs[i].a>mid&&qs[i].c>mid)tr[++c2]=qs[i]; else t[++c3]=qs[i]; for(int i=l;i<=l+c1-1;++i)qs[i]=tl[i-l+1]; for(int i=l+c1;i<=l+c1+c2-1;++i)qs[i]=tr[i-l-c1+1]; for(int i=l+c1+c2;i<=r;++i)qs[i]=t[i-l-c1-c2+1]; solve(l1,mid,l2,r2,l,l+c1-1); solve(mid+1,r1,l2,r2,l+c1,l+c1+c2-1); }else{ int mid=(l2+r2)/2; for(int i=l1;i<=r1;++i) for(int j=l2;j<=r2;++j)fg[id(i,j)]=1; for(int i=l1;i<=r1;++i){ spfa(l1,r1,l2,r2,id(i,mid)); for(int j=l;j<=r;++j) cmin(qs[j].ans,dis[id(qs[j].a,qs[j].b)]+dis[id(qs[j].c,qs[j].d)]); } for(int i=l1;i<=r1;++i) for(int j=l2;j<=r2;++j)fg[id(i,j)]=dis[id(i,j)]=0; int c1=0,c2=0,c3=0; for(int i=l;i<=r;++i) if(qs[i].b<=mid&&qs[i].d<=mid)tl[++c1]=qs[i]; else if(qs[i].b>mid&&qs[i].d>mid)tr[++c2]=qs[i]; else t[++c3]=qs[i]; for(int i=l;i<=l+c1-1;++i)qs[i]=tl[i-l+1]; for(int i=l+c1;i<=l+c1+c2-1;++i)qs[i]=tr[i-l-c1+1]; for(int i=l+c1+c2;i<=r;++i)qs[i]=t[i-l-c1-c2+1]; solve(l1,r1,l2,mid,l,l+c1-1); solve(l1,r1,mid+1,r2,l+c1,l+c1+c2-1); } } signed main(){ n=read(),m=read(); for(int i=1;i<=n;++i)for(int j=1;j<m;++j) addedge(id(i,j),id(i,j+1),read()); for(int i=1;i<n;++i)for(int j=1;j<=m;++j) addedge(id(i,j),id(i+1,j),read()); Q=read(); for(int i=1;i<=Q;++i) qs[i]=(Item){read(),read(),read(),read(),i,0x7fffffff}; solve(1,n,1,m,1,Q); sort(qs+1,qs+1+Q,[](Item a,Item b) {return a.pos<b.pos;}); for(int i=1;i<=Q;++i) print(qs[i].ans),*O++='\n'; fwrite(obuf,O-obuf,1,stdout); return 0; }