[BZOI2014]大融合——————线段树进阶

Posted keen_z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOI2014]大融合——————线段树进阶相关的知识,希望对你有一定的参考价值。

竟然改了不到一小时就改出来了, 可喜可贺

Description

Solution

一开始想的是边两侧简单路径之和的乘积,之后发现这是个树形结构,简单路径数就是节点数。

之后的难点就变成了如何求线段树中不连续且无序区间中的权值。答案当然是没办法求

所以我们要进行离线,现将所有建边信息记录下来,把最终形成的树建好,然后在树上求DFS序。这样就能保证一个子树内的节点编号是连续的。

在查询时给出的两点一定具有父子关系,只需先找出二者中的儿子,之后求出它们所在树的节点树与儿子子树的节点树,做差后相乘即可。

 

CODE:

 1 #include<bits/stdc++.h>
 2 #define debug cout<<"lbwnb"
 3 using namespace std;
 4 const int NN=1e5+10;
 5 int siz[NN],dep[NN],lin[NN],tmp,to[NN*2],n,q,rt[NN],x,y,fa[NN],dat[NN][3],nex[NN*2],head[NN],num;
 6 char op[NN];
 7 inline void add(int a,int b){
 8     to[++num]=b;    nex[num]=head[a];    head[a]=num;
 9     to[++num]=a;    nex[num]=head[b];    head[b]=num;
10 }
11 inline int getf(int x){
12     return fa[x]==x?x:fa[x]=getf(fa[x]);
13 }
14 inline int read(){
15     int x=0,f=1;
16     char ch=getchar();
17     while(ch<\'0\'||ch>\'9\'){
18         if(ch==\'-\')    f=-1;
19         ch=getchar();
20     }
21     while(ch>=\'0\'&&ch<=\'9\'){
22         x=(x<<1)+(x<<3)+(ch^48);
23         ch=getchar();
24     }
25     return x*f;
26 }
27 void write(int x){
28     if(x<0)    putchar(\'-\'), x=-x;
29     if(x>9)    write(x/10);
30     putchar(x%10+\'0\');
31 }
32 void dfs(int x){
33     lin[x]=++tmp;    siz[x]=1;
34     for(int i=head[x];i;i=nex[i]){
35         int v=to[i];
36         if(dep[v])    continue;
37         dep[v]=dep[x]+1;
38         dfs(v);
39         siz[x]+=siz[v];
40     }
41 }
42 struct node{
43     int ls[NN*40],rs[NN*40],seg,sum[NN*40];
44     void pushup(int x){
45         sum[x]=sum[ls[x]]+sum[rs[x]];
46     }
47     void insert(int &x,int l,int r,int pos,int val){
48         if(!x)    x=++seg;
49         if(l==r){ sum[x]+=val; return; }
50         int mid=(l+r)>>1;
51         if(pos<=mid)    insert(ls[x],l,mid,pos,val);
52         else    insert(rs[x],mid+1,r,pos,val);
53         pushup(x);
54     }    
55     void merge(int &x,int y,int l,int r){
56         if(!x||!y){ x=x+y; return; }
57         if(l==r){ sum[x]+=sum[y]; return; }
58         int mid=(l+r)>>1;
59         merge(ls[x],ls[y],l,mid);
60         merge(rs[x],rs[y],mid+1,r);
61         pushup(x);
62     }
63     int query(int x,int l,int r,int opl,int opr){
64         if(l>=opl&&r<=opr)    return sum[x];
65         int mid=(l+r)>>1,ret=0;
66         if(opl<=mid)    ret+=query(ls[x],l,mid,opl,opr);
67         if(opr>mid)        ret+=query(rs[x],mid+1,r,opl,opr);
68         return ret;
69     }
70 }segt;
71 int main(){
72     n=read(); q=read();
73     for(int i=1;i<=q;i++)
74         cin>>op[i]>>dat[i][1]>>dat[i][2];
75     for(int i=1;i<=q;i++)
76         if(op[i]==\'A\')    add(dat[i][1],dat[i][2]);
77     for(int i=1;i<=n;i++)
78         if(!dep[i])    dep[i]=1,dfs(i);
79     for(int i=1;i<=n;i++)
80         segt.insert(rt[i],1,n,lin[i],1), fa[i]=i;
81      for(int i=1;i<=q;i++){
82         if(op[i]==\'A\'){
83             int r1=getf(dat[i][1]), r2=getf(dat[i][2]);
84             segt.merge(rt[r1],rt[r2],1,n);
85             fa[r2]=r1;
86         }
87          if(op[i]==\'Q\'){
88             int son=dep[dat[i][1]]>dep[dat[i][2]]?dat[i][1]:dat[i][2];
89             int ro=getf(dat[i][1]);
90             int tot=segt.query(rt[ro],1,n,1,n),sot=segt.query(rt[ro],1,n,lin[son],lin[son]+siz[son]-1);
91             write((tot-sot)*sot);    putchar(\'\\n\');
92         }
93     }
94     return 0;
95 }
I LOVE SEGMENT_TREE

 

以上是关于[BZOI2014]大融合——————线段树进阶的主要内容,如果未能解决你的问题,请参考以下文章

[Bjoi2014]大融合

[BJOI2014]大融合

BZOJ4530[BJOI2014]大融合

4530. [BJOI2014]大融合LCT

刷题BZOJ 4530 [Bjoi2014]大融合

C++ 树进阶系列之线段树和它的延迟更新