luogu4219 and bzoj4530 大融合

题目描述

小强要在NN个孤立的星球上建立起一套通信系统。这套通信系统就是连接NN个点的一个树。 这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数量。

例如,在上图中,现在一共有了55条边。其中,(3,8)(3,8)这条边的负载是66,因 为有六条简单路径2-3-8238,2-3-8-72387,3-8,3-8-738,387,4-3-8438,4-3-8-74387路过了(3,8)(3,8)。

现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的 询问。

输入输出格式

输入格式:

 

第一行包含两个整数 N, QN,Q,表示星球的数量和操作的数量。星球从 11 开始编号。

接下来的 QQ 行,每行是如下两种格式之一:

  • A x y 表示在 xx和 yy 之间连一条边。保证之前 xx 和 yy是不联通的。
  • Q x y表示询问 (x,y)(x,y) 这条边上的负载。保证 xx 和 yy 之间有一条边。

 

输出格式:

 

对每个查询操作,输出被查询的边的负载。

 

输入输出样例

输入样例#1: 复制
8 6
A 2 3
A 3 4
A 3 8
A 8 7
A 6 5
Q 3 8
输出样例#1: 复制
6

说明

对于所有数据,1≤N,Q≤10^51N,Q105

 

本题可以树链剖分+并查集,先把树建好,然后每次加边x和y,假设x的深度小,那么我们通过并查集求出与x连接的最早的祖先。维护x到最早的祖先这个链(每个点加上y子树的点的个数)。 

  1 #include<bits/stdc++.h>
  2 using namespace std;  
  3 #define re register  
  4 #define R  re int
  5 #define rep(i,a,b) for(R i=a;i<=b;i++) 
  6 #define Rep(i,a,b) for(R i=a;i>=b;i--) 
  7 #define ms(i,a)    memset(a,i,sizeof(a))  
  8 #define V          inline void 
  9 #define I          inline int
 10 #define lc         (x<<1)
 11 #define rc         (x<<1|1)  
 12 #define mid        ((l+r)>>1)
 13 template<class T>V read(T &x){
 14   x=0;char c=getchar(); 
 15   while (!isdigit(c)) c=getchar(); 
 16   while (isdigit(c)) x=x*10+(c^48),c=getchar();  
 17 }
 18 template<class T>void write(T x){if(x>9) write(x/10);putchar(48+x%10);} 
 19 int const maxn=100001; 
 20 struct Edge{int to,nt;}E[maxn<<1];  
 21 struct Q{int x,y,z;}q[maxn]; 
 22 int n,m,top[maxn],id[maxn],f[maxn],sz[maxn],son[maxn],cnt,sum,H[maxn],dep[maxn],g[maxn];   
 23 struct SegT{
 24   int tg[maxn<<2],num[maxn<<2]; 
 25   void build(int x,int l,int r){
 26     if(l==r) {num[x]=1;return; }
 27     build(lc,l,mid); build(rc,mid+1,r);  
 28   }
 29   void pushd(int x){
 30     tg[lc]+=tg[x]; tg[rc]+=tg[x]; tg[x]=0;  
 31   }
 32   void update(int x,int l,int r,int ll,int rr,int v){
 33     if(l==ll && r==rr){ tg[x]+=v; return; }
 34     if(rr<=mid) update(lc,l,mid,ll,rr,v); 
 35     else if(ll>mid) update(rc,mid+1,r,ll,rr,v); 
 36     else update(lc,l,mid,ll,mid,v),update(rc,mid+1,r,mid+1,rr,v);  
 37   }
 38   int query(int x,int l,int r,int p){
 39     if(l==r) return num[x]+tg[x];  
 40     pushd(x) ;
 41     if(p<=mid) return query(lc,l,mid,p);  
 42     else return query(rc,mid+1,r,p);   
 43   }
 44 }T;
 45 void add(int a,int b){
 46   E[cnt]=(Edge){b,H[a]};H[a]=cnt++;  
 47   E[cnt]=(Edge){a,H[b]};H[b]=cnt++;  
 48 }
 49 int gf(int x){return x==g[x]? x:g[x]=gf(g[x]); }
 50 void dfs1(int x,int fa){
 51   sz[x]=1; dep[x]=dep[fa]+1; f[x]=fa;  
 52   for(R i=H[x];i!=-1;i=E[i].nt){
 53     int v=E[i].to;  
 54     if(v==fa) continue;  
 55     dfs1(v,x);  
 56     if(sz[son[x]]<sz[v]) son[x]=v;  
 57     sz[x]+=sz[v];  
 58   }
 59 }
 60 void dfs2(int x,int fa,int tp){
 61   top[x]=tp; id[x]=++sum;  
 62   if(son[x]) dfs2(son[x],x,tp); 
 63   for(R i=H[x];i!=-1;i=E[i].nt){
 64     int v=E[i].to;  
 65     if(v==fa || v==son[x]) continue; 
 66     dfs2(v,x,v);  
 67   }
 68 }
 69 void modify(int x,int y,int v){
 70   while(top[x]^top[y]){
 71     if(dep[top[x]]<dep[top[y]]) swap(x,y);  
 72     int tx=top[x];   
 73     T.update(1,1,n,id[tx],id[x],v);  
 74     x=f[tx];  
 75   }
 76   if(dep[x]<dep[y]) swap(x,y);  
 77   T.update(1,1,n,id[y],id[x],v);  
 78 }
 79 int main(){
 80   scanf("%d%d",&n,&m);ms(-1,H);  
 81   rep(i,1,m){
 82     char s[2]; scanf("%s",s); scanf("%d%d",&q[i].x,&q[i].y); 
 83     if(s[0]=='A') q[i].z=1,add(q[i].x,q[i].y);  
 84   }
 85   rep(i,1,n) if(!sz[i])  dfs1(i,i);  
 86   rep(i,1,n) if(!id[i])  dfs2(i,i,i);  
 87   T.build(1,1,n);  
 88   rep(i,1,n) g[i]=i;  
 89   rep(i,1,m){
 90     if(dep[q[i].x]> dep[q[i].y]) swap(q[i].x,q[i].y);  
 91     int x=gf(q[i].x),y=q[i].y;  
 92     if(q[i].z==1){
 93       int t=T.query(1,1,n,id[y]);
 94       modify(x,q[i].x,t); g[y]=x;   
 95     }else {
 96       int t1=T.query(1,1,n,id[y]);  
 97       int t2=T.query(1,1,n,id[x]);  
 98       printf("%d\n",t1*(t2-t1));  
 99     }
100   }
101   return 0; 
102 }
View Code

 

此题也可以直接lct,维护子树信息即可。 

LCT维护子树信息

学了大神的LCT维护子树信息的方式,觉得还算好理解,于是自己yy了这道题。

我们知道,在LCT中的Splay Tree中,access某个点并splay到根,那么它的实儿子记录的信息是这条链的信息,并不是我们想要的子树信息。

而所有实儿子和虚儿子的信息才是我们想要求的子树信息。

但是由于虚儿子“儿子认爹,爹不认儿子”的性质,无法在pushup的时候上传信息。

事实上,我们注意到,对于Splay Tree的所有基本操作,除了access和link以外,都不会对虚儿子的信息进行修改。

那么我们每次在添加虚儿子时,顺便把虚儿子的信息也记录到父亲节点中。

这样我们每次调用一个节点时,将它Splay Tree中实儿子的信息,加上它自身的虚儿子的信息,就是我们想要的子树信息。

于是我们对于每个节点记录两个信息:它的总信息和它虚儿子的信息,pushup时更新x的总信息为:x实儿子的总信息+x虚儿子的信息+x本身的信息。

按照这种方法我们来思考这道题,可以发现所求的答案就是一条边两端点的子树大小乘积,我们把某一个端点定为整棵树的根,可以知道整棵树的大小,而根据另一个节点可以知道一个子树的大小,相减即为另一个子树的大小。

具体的实现:

access操作中割断了实边c[1][x],该边变为了虚边,所以应该加到x的虚儿子信息中,加入了实边t,该边不再是虚边,所以应从x的虚儿子信息中减去。

link操作中为了在加入x时同时更新y的信息,需要makeroot(x),makeroot(y),然后连x->y的虚边(实际上只需要access(y)和splay(y))。

其余的操作,和普通的LCT没有任何区别。

代码中需要注意的是,sum[x]存的是总信息(子树大小),si[x]存的是虚儿子信息(子树除了链以外的大小),不要弄混。

 1 #include<bits/stdc++.h>
 2 using namespace std;  
 3 #define re register 
 4 #define R  re int 
 5 #define rep(i,a,b) for(R i=a;i<=b;i++) 
 6 #define Rep(i,a,b) for(R i=a;i>=b;i--) 
 7 #define ms(i,a)    memset(a,i,sizeof(a)) 
 8 #define lc         ch[x][0]
 9 #define rc         ch[x][1] 
10 #define pa         fa[x] 
11 #define I          inline int
12 template<class T>I read(T &x){
13   x=0; char c=0;  
14   while (!isdigit(c)) c=getchar(); 
15   while (isdigit(c)) x=x*10+(c^48),c=getchar();  
16 }
17 int const maxn=100001; 
18 int st[maxn],n,m,top;  
19 struct Lct{
20   int ch[maxn][2],s[maxn],si[maxn],r[maxn],fa[maxn];  
21   I get(int x){return ch[pa][1]==x;}
22   I isr(int x){return ch[pa][0]!=x && ch[pa][1]!=x;} 
23   I update(int x){ s[x]=s[lc]+s[rc]+si[x]+1;}
24   I pushd(int x){
25     if(!r[x]) return 0;  
26     swap(ch[lc][0],ch[lc][1]); 
27     swap(ch[rc][0],ch[rc][1]);  
28     r[lc]^=1;r[rc]^=1;r[x]=0;  
29   }
30   I con(int x,int y,int z){fa[x]=y; ch[y][z]=x;}
31   I rotate(int x){
32     int f=fa[x],g=fa[f],c=get(x),cc=get(f);  
33     if(!isr(f)) ch[g][cc]=x; fa[x]=g; 
34     con(ch[x][c^1],f,c); con(f,x,c^1); 
35     update(f); update(x);  
36   }
37   I splay(int x){
38     top=0; int k=x; while (!isr(k)) st[++top]=k,k=fa[k];st[++top]=k; 
39     while (top)  pushd(st[top--]);  
40     for(; !isr(x); rotate(x)) if(!isr(pa))  rotate(get(x)==get(pa)?  pa:x);  
41   }
42   I access(int x){
43     for(int y=0;x ;y=x,x=pa){
44       splay(x); si[x]+=s[rc]; si[x]-=s[rc=y]; update(x);  
45     }
46   }
47   I make(int x){access(x); splay(x); r[x]^=1; swap(lc,rc);}
48   I find(int x){access(x); splay(x); while (lc) x=lc; return x;}
49   I split(int x,int y){make(x); access(y); splay(y);   }
50   I link(int x,int y){
51     split(x,y);si[fa[x]=y]+=s[x]; update(y);  
52   }
53 }lct; 
54 int main(){
55   read(n); read(m); 
56   while (m--){
57     char s[2]; int x,y; 
58     scanf("%s",s); read(x); read(y); 
59     if(s[0]=='A') lct.link(x,y); 
60     else {
61       lct.split(x,y);  
62       printf("%d\n",(lct.si[x]+1)*(lct.si[y]+1));  
63     }
64   }
65   return 0;  
66 }
View Code

 

posted @ 2018-12-18 15:57  zjxxcn  阅读(229)  评论(0编辑  收藏  举报