LOJ 2718「NOI2018」归程

【题解】

  本题有多种做法,例如可持久化并查集、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].

猜你喜欢

转载自www.cnblogs.com/DriverLao/p/9789092.html