1. 程式人生 > >LOJ 2718「NOI2018」歸程

LOJ 2718「NOI2018」歸程

info pos mes efi name 題解 put 最大 答案

技術分享圖片

【題解】

  本題有多種做法,例如可持久化並查集、kruskal重構樹等。

  kruskal重構樹的做法是這樣的:先把邊按照海拔h從大到小的順序排序,然後跑kruskal建立海拔的最大生成樹,順便建kruskal重構樹。

  這樣建出來的重構樹是一個小根堆,也就是說,如果某個節點沒有被淹,它的子樹內的點都不會被淹,它們可以互相開車到達。

  我們建重構樹的時候維護每個節點的子樹內的點到1號點的最小距離mn,mn先用dijkstra處理好。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<algorithm>
  4
#define LL long long 5 #define rg register 6 #define N 400010 7 using namespace std; 8 int T,n,m,q,k,s,tot,cnt,last[N],f[N],pos[N],p[N][20],hei[N]; 9 LL ans,mn[N]; 10 struct edge{ 11 int to,pre,dis; 12 }e[N<<1]; 13 struct rec{ 14 int u,v,h; 15 }r[N<<1]; 16 struct heap{
17 LL d;int p; 18 }h[N]; 19 inline int read(){ 20 int k=0,f=1; char c=getchar(); 21 while(c<0||c>9)c==-&&(f=-1),c=getchar(); 22 while(0<=c&&c<=9)k=k*10+c-0,c=getchar(); 23 return k*f; 24 } 25 inline void up(int x){ 26 int fa; 27 while
((fa=(x>>1))&&h[fa].d>h[x].d) swap(h[x],h[fa]),swap(pos[h[x].p],pos[h[fa].p]),x=fa; 28 } 29 inline void down(int x){ 30 int son; 31 while((son=(x<<1))<=tot){ 32 if(son<tot&&h[son].d>h[son+1].d) son++; 33 if(h[son].d<h[x].d) swap(h[x],h[son]),swap(pos[h[x].p],pos[h[son].p]),x=son; 34 else return; 35 } 36 } 37 inline void dijkstra(int x){ 38 for(rg int i=1;i<=(n<<1);i++) mn[i]=4e9; 39 h[tot=pos[x]=1]=(heap){mn[x]=0,x}; 40 while(tot){ 41 int now=h[1].p; pos[h[tot].p]=1; h[1]=h[tot--]; if(tot) down(1); 42 for(rg int i=last[now],to;i;i=e[i].pre) 43 if(mn[to=e[i].to]>mn[now]+e[i].dis){ 44 mn[to]=mn[now]+e[i].dis; 45 if(!pos[to]) h[pos[to]=++tot]=(heap){mn[to],to}; 46 else h[pos[to]].d=mn[to]; 47 up(pos[to]); 48 } 49 } 50 } 51 inline int climb(int x,int y){ 52 for(rg int i=19;i>=0;i--) if(hei[p[x][i]]>y) x=p[x][i]; return x; 53 } 54 int find(int x){return f[x]==x?x:f[x]=find(f[x]);} 55 inline bool cmp(rec a,rec b){return a.h>b.h;} 56 inline void Pre(){ 57 tot=0; cnt=0; ans=0; 58 memset(last,0,sizeof(last)); 59 memset(pos,0,sizeof(pos)); 60 memset(p,0,sizeof(p)); 61 } 62 int main(){ 63 freopen("return.in","r",stdin); 64 freopen("return.out","w",stdout); 65 T=read(); 66 while(T--){ 67 Pre(); 68 n=read(); m=read(); 69 for(rg int i=1;i<=m;i++){ 70 int u,v,d; 71 r[i].u=u=read(),r[i].v=v=read(),d=read(),r[i].h=read(); 72 e[++tot]=(edge){v,last[u],d}; last[u]=tot; 73 e[++tot]=(edge){u,last[v],d}; last[v]=tot; 74 } 75 dijkstra(1); 76 // for(rg int i=1;i<=n;i++) printf("%d ",mn[i]); puts("mn"); 77 tot=n; 78 sort(r+1,r+1+m,cmp); 79 for(rg int i=1;i<=n;i++) f[i]=i,f[i+n]=i+n; 80 for(rg int i=1;i<=m;i++){ 81 int u=find(r[i].u),v=find(r[i].v); 82 if(u!=v){ 83 hei[++tot]=r[i].h; mn[tot]=min(mn[u],mn[v]); 84 f[u]=f[v]=p[u][0]=p[v][0]=tot; 85 cnt++; 86 } 87 if(cnt==n-1) break; 88 } 89 for(rg int j=1;j<20;j++) 90 for(rg int i=1;i<=tot;i++) p[i][j]=p[p[i][j-1]][j-1]; 91 92 q=read(); k=read(); s=read(); 93 while(q--){ 94 int v0=(read()+k*ans-1)%n+1,p0=(read()+k*ans)%(s+1); 95 // printf("v0=%d p0=%d\n",v0,p0); 96 printf("%lld\n",ans=mn[climb(v0,p0)]); 97 } 98 } 99 return 0; 100 }

  那麽對於每個出發點為v的詢問,我們倍增找到它最早的不被淹的祖先p,答案就是mn[p].

LOJ 2718「NOI2018」歸程