[bzoj1969] [Ahoi2005]LANE 航线规划

  tarjan、并查集、树状数组、树链剖分。

  时间倒流,变删边为加边。

  先求一波边双联通分量,缩点。

  题目保证最后还是整张图联通的。。所以就是一棵树。

  现在的操作就是,将路径上的边权置0(加边时),查询两点间边权和。

  可以转换成求根到点路径上边权和,置0的时候,就相当于将子树内的值都减去原边的长度,可以dfs序+树状数组。

  置0那个还要写个并查集,维护当前点经过连续一段0边到达的最远的祖先。

  查询的时候还得求lca。。。。就顺便写个链剖...

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<algorithm>
  5 #define ui unsigned int
  6 using namespace std;
  7 const int maxn=30023,maxm=100023;
  8 struct zs{int too,pre;}e[maxm<<1];int tot,last[maxn];
  9 bool iscut[maxm<<1];
 10 struct zs1{int x,y;}a[maxm];
 11 struct zs2{bool del;int id,x,y;}b[40023];
 12 int fa[maxn],bel[maxn],dep[maxn],dfn[maxn],low[maxn],tim,L[maxn],R[maxn],TIM,sz[maxn],cha[maxn],id[maxn];
 13 int la[maxn<<1],pre[maxn<<1],too[maxn<<1],tt;
 14 int An[40023];
 15 int i,j,k,n,m,q,cnt;
 16 bool gg[maxm];
 17
 18 int ra,fh;char rx;
 19 inline int read(){
 20     rx=getchar(),ra=0,fh=1;
 21     while((rx<‘0‘||rx>‘9‘)&&rx!=‘-‘)rx=getchar();
 22     if(rx==‘-‘)fh=-1,rx=getchar();
 23     while(rx>=‘0‘&&rx<=‘9‘)ra*=10,ra+=rx-48,rx=getchar();return ra*fh;
 24 }
 25
 26 inline int getbel(int x){return bel[x]!=x?bel[x]=getbel(bel[x]):x;}
 27
 28 void dfs(int x,int efa){
 29     dfn[x]=low[x]=++tim;
 30     for(int i=last[x];i;i=e[i].pre)if(i!=efa)
 31         if(!dfn[e[i].too])
 32             dfs(e[i].too,i^1),low[x]=min(low[x],low[e[i].too]);
 33         else low[x]=min(low[x],dfn[e[i].too]);
 34     if(dfn[x]==low[x]&&efa)iscut[efa]=iscut[efa^1]=1;
 35 }
 36 void dfs2(int x){
 37     dep[x]=dep[fa[x]]+1,sz[x]=1;
 38     for(int i=la[x];i;i=pre[i])
 39         if(too[i]!=fa[x])fa[too[i]]=x,dfs2(too[i]),sz[x]+=sz[too[i]];
 40 }
 41 void dfs3(int x,int chain){
 42     cha[x]=chain,L[x]=++TIM;
 43     int i,mx=0;
 44     for(i=la[x];i;i=pre[i])if(too[i]!=fa[x]&&sz[too[i]]>sz[mx])mx=too[i];
 45     if(!mx){R[x]=TIM;return;}
 46     dfs3(mx,chain);
 47     for(i=la[x];i;i=pre[i])if(too[i]!=fa[x]&&too[i]!=mx)dfs3(too[i],too[i]);
 48     R[x]=TIM;
 49 }
 50 inline int getlca(int a,int b){
 51     while(cha[a]!=cha[b]){
 52         if(dep[cha[a]]<dep[cha[b]])swap(a,b);
 53         a=fa[cha[a]];
 54     }
 55     return dep[a]<dep[b]?a:b;
 56 }
 57 inline void insert(int a,int b){
 58     e[++tot].too=b,e[tot].pre=last[a],last[a]=tot,
 59     e[++tot].too=a,e[tot].pre=last[b],last[b]=tot;
 60 }
 61 inline void ins(int a,int b){//printf(" %d-->%d\n",a,b);
 62     too[++tt]=b,pre[tt]=la[a],la[a]=tt,
 63     too[++tt]=a,pre[tt]=la[b],la[b]=tt;
 64 }
 65 int t[maxn];
 66 inline void add(int l,int r,int v){//printf("       add:  %d--%d  v:%d\n",l,r,v);
 67     while(l<=cnt)t[l]-=v,l+=l&-l;
 68     r++;while(r<=cnt)t[r]+=v,r+=r&-r;
 69 }
 70 inline int ask(int x){/*printf("__x:  %d\n",x);*/int sm=0;while(x)sm+=t[x],x-=x&-x;return sm;}
 71
 72 inline int query(int x,int y){
 73     int lca=getlca(x,y);//printf("query:  %d %d  lca:%d   %d %d\n",x,y,lca,getbel(x),getbel(y));
 74     x=dep[x]+ask(L[x]),y=dep[y]+ask(L[y]),lca=dep[lca]+ask(L[lca]);//printf("dep:   %d %d %d\n",x,y,lca);
 75     return x+y-(lca<<1);
 76 }
 77 inline void link(int x,int y){//printf("link:%d   %d\n",x,y);
 78     int i,j,tmp;
 79     i=getbel(x),j=getbel(y);//printf("    %d %d   dep:%d %d\n",i,j,dep[i],dep[j]);
 80     while(i!=j){
 81         if(dep[i]<dep[j])swap(i,j);
 82         tmp=getbel(fa[i]);
 83         add(L[i],R[i],1/*dep[i]-dep[tmp]*/);//printf("i:%d   tmp:%d\n",i,tmp);
 84         bel[i]=tmp,i=bel[i];
 85     }
 86 }
 87
 88
 89 bool cmp(zs1 a,zs1 b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
 90 inline int getpos(int x,int y){
 91     int l=1,r=m,mid;
 92     while(l<r)
 93         if(a[(mid=l+r>>1)].x==x?(a[mid].y==y?gg[mid]:a[mid].y<y):a[mid].x<x)l=mid+1;else r=mid;
 94     return l;
 95 }
 96 int main(){
 97     n=read(),m=read();register int i;
 98     for(i=1;i<=m;i++){
 99         j=read(),k=read();
100         if(j>k)swap(j,k);
101         a[i].x=j,a[i].y=k;
102     }
103     sort(a+1,a+1+m,cmp);
104     int id1;i=0;
105     for(id1=read();id1!=-1;id1=read()){
106         i++,j=b[i].x=read(),k=b[i].y=read();
107         if(j>k)swap(j,k);
108         if(!id1)b[i].del=1,gg[b[i].id=getpos(j,k)]=1;
109     }q=i;
110
111     tot=1;
112     for(i=1;i<=m;i++)if(!gg[i])insert(a[i].x,a[i].y);
113     dfs(1,0);
114     for(i=1;i<=n;i++)bel[i]=i;
115     for(i=2;i<=tot;i+=2)if(!iscut[i])
116         bel[getbel(e[i].too)]=getbel(e[i^1].too);
117     for(i=1;i<=n;i++)if(getbel(i)==i)id[i]=++cnt;
118     for(i=1;i<=q;i++){
119         if(b[i].del)a[b[i].id].x=id[bel[a[b[i].id].x]],a[b[i].id].y=id[bel[a[b[i].id].y]];
120         b[i].x=id[getbel(b[i].x)],b[i].y=id[getbel(b[i].y)];
121     }
122     for(i=2;i<=tot;i+=2)if(iscut[i])ins(id[bel[e[i].too]],id[bel[e[i^1].too]]);
123     for(i=1;i<=cnt;i++)bel[i]=i;
124     fa[1]=0,dfs2(1),dfs3(1,1);
125 //    for(i=1;i<=cnt;i++)printf("    i:%d   dfn:%d   bel:%d   L:%d  R:%d   dep:%d\n",i,L[i],cha[i],L[i],R[i],dep[i]);
126
127     for(i=q;i;i--)//{
128         if(!b[i].del)An[i]=query(b[i].x,b[i].y);
129         else link(a[b[i].id].x,a[b[i].id].y);
130 //      for(j=1;j<=n;j++)printf("   %d",ask(j));puts("");
131 //  }
132     for(i=1;i<=q;i++)if(!b[i].del)printf("%d\n",An[i]);
133 }
134 

时间: 2024-11-09 00:15:28

[bzoj1969] [Ahoi2005]LANE 航线规划的相关文章

BZOJ1969: [Ahoi2005]LANE 航线规划(LCT)

Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 587  Solved: 259[Submit][Status][Discuss] Description 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一

bzoj1969 [Ahoi2005]LANE 航线规划 树链剖分

题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=1969 题解 如果我们把整个图边双联通地缩点,那么最终会形成一棵树的样子. 那么在这棵树上,\(x\) 和 \(y\) 两个点的答案就是它们之间的不在环中的边的数量. 现在考虑动态维护每一条边在不在环中.发现动态删除的话不太好做,所以时光反转,改成插入一条边. 先随便建立一棵生成树,然后如果插入一条非树边,那么两个端点之间的边就都是在环中的边了. 用树剖维护就可以了. 时间复杂度 \(O(q\

【BZOJ1969】[Ahoi2005]LANE 航线规划 离线+树链剖分+线段树

[BZOJ1969][Ahoi2005]LANE 航线规划 Description 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到

BZOJ 1969: [Ahoi2005]LANE 航线规划( 树链剖分 )

首先我们要时光倒流, 倒着做, 变成加边操作维护关键边. 先随意搞出一颗树, 树上每条边都是关键边(因为是树, 去掉就不连通了)....然后加边(u, v)时, 路径(u, v)上的所有边都变成非关键边了, 因为形成了环, 环上任意2点有2条路径...下图, 加上蓝色的边, 红色x的边就变成了非关键边. 所以树链剖分维护一下...时间复杂度O(NlogN+MlogM+Qlog^2N), 可以AC. 翻了翻ZY的标解:“动态维护树+最近公共祖先查询”....复杂度是O(NlogN+M+QlogN)

【bzoj1959】[Ahoi2005]LANE 航线规划 离线处理+树链剖分+线段树

题目描述 对Samuel星球的探险已经取得了非常巨大的成就,于是科学家们将目光投向了Samuel星球所在的星系——一个巨大的由千百万星球构成的Samuel星系. 星际空间站的Samuel II巨型计算机经过长期探测,已经锁定了Samuel星系中许多星球的空间坐标,并对这些星球从1开始编号1.2.3……. 一些先遣飞船已经出发,在星球之间开辟探险航线. 探险航线是双向的,例如从1号星球到3号星球开辟探险航线,那么从3号星球到1号星球也可以使用这条航线. 例如下图所示: 在5个星球之间,有5条探险航

BZOJ 1969: [Ahoi2005]LANE 航线规划 [树链剖分 时间倒流]

题意: 一张图,删除边,求两点之间的割边数量.保证任意时刻图连通 任求一棵生成树,只有树边可能是割边 时间倒流,加入一条边,就是两点路径上的边都不可能是割边,区间覆盖... 然后本题需要把边哈希一下,手写哈希比map快很多 貌似还有一种不用树剖的做法,不管了 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; co

[AHOI2005]航线规划(树链剖分+时间倒流)

传送门 练一下树剖的板子,运用一下时间倒流和下放边权的思想. 题中所谓“关键航线”其实就是桥. 删边操作桥不好维护,但如果是加边,每加一条边,两点作为端点的这条路径就都不再是桥----->考虑时间倒流. 从后往前,每删除一条边,现在就是加边,该路径上所有边都不是桥(打上标记). 可以先求出一棵最小生成树(代码中是在dfs中实现的)那些多的边就以加边的方式加入(说明到最后一个操作后,这条路径的边也不是桥). #include<bits/stdc++.h> #define M 200003

Luogu2542 AHOI2005 航线规划 树链剖分、线段树

传送门 看到删边不用想就是反着加边 先把删完边之后的图拆一个生成树出来,然后考虑非树边的影响.实际上非树边就是让树上的一条路径的权值从$1$变为了$0$,而每一个询问就是一条路径上的权值之和.使用树链剖分+线段树维护权值即可. 1 #include<bits/stdc++.h> 2 #define lch (now << 1) 3 #define rch (now << 1 | 1) 4 #define mid ((l + r) >> 1) 5 //This

[AHOI2005]航线规划

嘟嘟嘟 好久不写树剖,细节有点小问题. 这题比较好想.看到删边,一般就能想到离线加边. 然后考虑如果一条边是关键边,那么他一定是一个桥.因此首先要做的是边双缩点. 缩完点后图就变成了树.至于加边,显然就是把这条边所在环上的点缩成了一个点.但如果再暴力缩点的话会超时. 实际上相当于把树上在环中的边的边权改成了0.然后询问的时候就是树上两点间距离了. 于是上树剖. 细节就是树剖的时候不要改lca.这点我注意到了,关键是每一次我都没有改链的顶端结点-- #include<cstdio> #inclu