zoukankan      html  css  js  c++  java
  • 洛谷T3401 洛谷树 树剖&&分治

    这道题好干燥啊。。。折腾了半个月。。。感谢bogo大佬对我的指导。。。
    题目要求支持的操作:1.查询某段路径的所有子路径的xor值之和;2.修改某条边的权值。重点是操作1。
    刚开始,我看到了操作1之后就不自觉的想到了非~常暴力的东西。。。还好大佬及时把我引上正途:分治!


    大家知道,最大子段和有个分治算法,本题的方法就跟这个比较类似。
    对于一段子路径,若它能对答案产生贡献,那么它要么完全在左儿子中,要么完全在右儿子中,要么跨越左右儿子。
    对于每段路径,我们需要记录如下变量:yh:异或和,ans:答案,就是要查询的东西,p0[i]:此序列的前缀序列中,异或和的二进制第i位为0的序列有多少段,后面的p1,s0,s1类似。
    于是,在分治的合并阶段,答案便分为两个部分:第一部分是左右儿子返回的ans;第二部分是左儿子的s0[i]*p1[i]和s1[i]*p0[i],这两个结果还要再乘以(1 << i),表示有多少段跨越左右儿子的子路径的xor值的二进制第i位为1,乘上(1 << i)之后就表示答案实际应该累加的值。因为0和1、1和0异或的结果是1嘛,因此就对答案产生了贡献。
    我们当然也要维护p0、p1、s0、s1。这里较上面简单一些,细节详见代码的rg_a结构体定义部分。
    以上讨论的都是链上的做法,题目给定的是一棵树,那么树剖就Ok了,之后扔到线段树里大力merge即可~
    对于修改操作,在线段树底层重建节点,然后顺次merge其所有祖先即可。


    下面说几个疑难的问题:
    一.要建线段树,要求权值在点上,但题目却说在边上。怎么办呢?可以把每条边的边权下放到树中此条边下方连接的节点上。这样,根节点就不会被下放,但是并不影响结果,至于为什么,会在下面提到。
    二.查询时,对于一段路径u->lca->v,因为lca也被下放了权值,但是它对应的边并不在u->v路径中,因此不能被统计,所以查询时只统计路径上除lca之外的点。鉴于此,上面提到的根节点便无所谓是否下放了。具体操作时,在树剖“跳”的过程的末端稍加修改即可。
    三.这点非常重要!
    我在做这题时,前几份代码狂WA不止,后来找到原因:merge操作不满足交换律,但是我在查询时却出现了运算顺序的漏洞。经过一番脑洞,我找到了正确的运算顺序,现描述如下:
    1.将树剖的待合并结果分成u->lca和lca->v两部分,存入数组TL和TR;
    2.将TL和TR的结果分别全部合并到TL[1]和TR[1]中(注意这里的运算顺序,建议手动画图验证);
    3.进行特判,如果TL为空,那么直接返回TR[1].ans,反之亦然;
    4.若TL、TR均非空,则先将TL[1]进行“翻转”(细节见代码,同样建议画图验证),然后合并TL[1]、TR[1],返回合并后的ans即可。
    四.有个小坑,或许是我不够细心吧,那就是查询的路径的起点和终点有可能是同一个点。开始没注意这个,结果导致WA成70分。所以我在查询时加了个特判,若u==v则直接返回0。
    代码如下,又丑又长,见谅见谅:

      1 #include<cstdio>
      2 #include<iostream>
      3 #include<cstring>
      4 #include<cmath>
      5 #include<ctime>
      6 #include<cstdlib>
      7 
      8 #include<string>
      9 #include<stack>
     10 #include<queue>
     11 #include<vector>
     12 #include<algorithm>
     13 #include<map>
     14 #include<set>
     15 
     16 #define inf 2147483647
     17 #define ri register int
     18 #define ll long long
     19 
     20 #define mid (l+r>>1)
     21 #define lson (o<<1)
     22 #define rson ((o<<1)+1)
     23 
     24 using namespace std;
     25 
     26 inline void read(int &x){
     27     x=0;
     28     char t=getchar();
     29     bool f=0;
     30     
     31     while(t<'0' || t>'9'){
     32         if(t=='-')f=1;
     33         t=getchar();
     34     }
     35     
     36     while(t>='0' && t<='9'){
     37         x=(x<<3)+(x<<1)+t-'0';
     38         t=getchar();
     39     }
     40     
     41     if(f)x=-x;
     42 }
     43 
     44 inline void addedge(int,int,int);
     45 int u[60005];
     46 int v[60005];
     47 int w[60005];
     48 int fi[30005];
     49 int ne[60005];
     50 int pe=0;  //无向邻接表 
     51 
     52 int wp[30005];  //下放的点权 
     53 
     54 void dfs1(int);
     55 int fa[30005];  //父亲 
     56 int dep[30005];  //深度 
     57 int size[30005];  //子树大小 
     58 int son[30005];  //重儿子 
     59 
     60 void dfs2(int);
     61 int top[30005];  //链顶节点 
     62 int dfsx[30005];  //dfs序 
     63 int xu=0;
     64 
     65 int pos[30005];  //节点位置,in Sgt.
     66 
     67 struct rg_a{
     68     ll yh;  //异或和 
     69     ll ans;  //答案 
     70     ll p0[10],p1[10],s0[10],s1[10];  //本段区间二进制第j位为0/1的前/后缀区间数 
     71     
     72     inline void merge(rg_a &A,rg_a &B){  //合并 
     73         rg_a T;
     74         int bl,br;
     75         
     76         T.yh=A.yh^B.yh;
     77         T.ans=A.ans+B.ans;
     78         
     79         for(ri i=0;i<10;i++){
     80             T.ans+=A.s0[i]*B.p1[i]*(1<<i);
     81             T.ans+=A.s1[i]*B.p0[i]*(1<<i);
     82             
     83             bl=(A.yh>>i)&1;
     84             br=(B.yh>>i)&1;
     85             
     86             T.p0[i]=A.p0[i];
     87             if(bl)T.p0[i]+=B.p1[i];
     88             else T.p0[i]+=B.p0[i];
     89             
     90             T.p1[i]=A.p1[i];
     91             if(bl)T.p1[i]+=B.p0[i];
     92             else T.p1[i]+=B.p1[i];
     93             
     94             T.s0[i]=B.s0[i];
     95             if(br)T.s0[i]+=A.s1[i];
     96             else T.s0[i]+=A.s0[i];
     97             
     98             T.s1[i]=B.s1[i];
     99             if(br)T.s1[i]+=A.s0[i];
    100             else T.s1[i]+=A.s1[i];
    101         }
    102         
    103         *this=T;
    104     }
    105 };
    106 
    107 struct sgt{  //线段树
    108     rg_a node[120005];
    109     
    110     void build(int o,int l,int r){
    111         if(l==r){
    112             node[o].yh=node[o].ans=wp[dfsx[l]];
    113             
    114             for(ri i=0;i<10;i++){
    115                 node[o].p0[i]=node[o].s0[i]=((node[o].yh>>i)&1)^1;
    116                 node[o].p1[i]=node[o].s1[i]=(node[o].yh>>i)&1;
    117             }
    118         }
    119         else{
    120             build(lson,l,mid);
    121             build(rson,mid+1,r);
    122             
    123             node[o].merge(node[lson],node[rson]);
    124         }
    125     }
    126     
    127     void update(int o,int l,int r,int p,int x){
    128         if(l==r && l==p){
    129             node[o].yh=node[o].ans=x;
    130             
    131             for(ri i=0;i<10;i++){
    132                 node[o].p0[i]=node[o].s0[i]=((x>>i)&1)^1;
    133                 node[o].p1[i]=node[o].s1[i]=(x>>i)&1;
    134             }
    135         }
    136         else{
    137             if(p<=mid)update(lson,l,mid,p,x);
    138             else update(rson,mid+1,r,p,x);
    139             
    140             node[o].merge(node[lson],node[rson]);
    141         }
    142     }
    143     
    144     rg_a query(int o,int l,int r,int a,int b){
    145         if(l>=a && r<=b)return node[o];
    146         else{
    147             if(b<=mid)return query(lson,l,mid,a,b);
    148             else if(a>mid)return query(rson,mid+1,r,a,b);
    149             else{
    150                 rg_a tl=query(lson,l,mid,a,b);
    151                 rg_a tr=query(rson,mid+1,r,a,b);
    152                 
    153                 tl.merge(tl,tr);
    154                 return tl;
    155             }
    156         }
    157     }
    158 } tree;
    159 
    160 inline ll path_query(int,int);
    161 rg_a TL[50],TR[50];  //外层查询临时结果
    162 int pl,pr;  //记录TL和TR存放的临时结果的数量
    163 
    164 int n,q;
    165 int f,x,y,z;
    166 int root;
    167 
    168 int main(){
    169     srand(time(0)+19260817);
    170     
    171     read(n);read(q);
    172     root=rand()%n+1;  //Ha~
    173     
    174     for(ri i=1;i<n;i++){
    175         read(x);read(y);read(z);
    176         addedge(x,y,z);
    177         addedge(y,x,z);
    178     }
    179     
    180     fa[root]=0;
    181     dep[root]=1;
    182     wp[root]=0;
    183     dfs1(root);
    184     
    185     top[root]=root;
    186     dfs2(root);
    187     
    188     for(ri i=1;i<=n;i++)pos[dfsx[i]]=i;
    189     
    190     tree.build(1,1,n);
    191     
    192     while(q--){
    193         read(f);
    194         
    195         if(f==1){
    196             read(x);read(y);
    197             printf("%lld
    ",path_query(x,y));
    198         }
    199         else{
    200             read(x);read(y);read(z);
    201             if(pos[x]<pos[y])tree.update(1,1,n,pos[y],z);
    202             else tree.update(1,1,n,pos[x],z);
    203         }
    204     }
    205     
    206     return 0;
    207 }
    208 
    209 inline void addedge(int x,int y,int z){
    210     pe++;
    211     u[pe]=x;
    212     v[pe]=y;
    213     w[pe]=z;
    214     ne[pe]=fi[x];
    215     fi[x]=pe;
    216 }
    217 
    218 void dfs1(int s){
    219     size[s]=1;
    220     int maxson=0;
    221     
    222     int t=fi[s];
    223     int to=v[t];
    224     
    225     while(t){
    226         if(to!=fa[s]){
    227             fa[to]=s;
    228             dep[to]=dep[s]+1;
    229             wp[to]=w[t];
    230             
    231             dfs1(to);
    232             
    233             size[s]+=size[to];
    234             if(size[to]>maxson){
    235                 son[s]=to;
    236                 maxson=size[to];
    237             }
    238         }
    239         
    240         t=ne[t];
    241         to=v[t];
    242     }
    243 }
    244 
    245 void dfs2(int s){
    246     xu++;
    247     dfsx[xu]=s;
    248     
    249     if(son[s]){
    250         top[son[s]]=top[s];
    251         dfs2(son[s]);
    252     }
    253     
    254     int t=fi[s];
    255     int to=v[t];
    256     
    257     while(t){
    258         if(to!=fa[s] && to!=son[s]){
    259             top[to]=to;
    260             dfs2(to);
    261         }
    262         
    263         t=ne[t];
    264         to=v[t];
    265     }
    266 }
    267 
    268 inline ll path_query(int x,int y){
    269     if(x==y)return 0;  //特判起终点相同
    270     
    271     pl=pr=0;  //重置临时结果数组
    272     
    273     int tx=top[x],ty=top[y];
    274     
    275     while(tx!=ty){
    276         if(dep[tx]>dep[ty]){
    277             pl++;
    278             TL[pl]=tree.query(1,1,n,pos[tx],pos[x]);
    279             x=fa[tx];
    280             tx=top[x];
    281         }
    282         else{
    283             pr++;
    284             TR[pr]=tree.query(1,1,n,pos[ty],pos[y]);
    285             y=fa[ty];
    286             ty=top[y];
    287         }
    288     }
    289     
    290     if(x!=y){
    291         if(pos[x]<pos[y]){
    292             pr++;
    293             TR[pr]=tree.query(1,1,n,pos[x]+1,pos[y]);
    294         }
    295         else{
    296             pl++;
    297             TL[pl]=tree.query(1,1,n,pos[y]+1,pos[x]);
    298         }
    299     }
    300     
    301     for(ri i=2;i<=pl;i++)TL[1].merge(TL[i],TL[1]);
    302     for(ri i=2;i<=pr;i++)TR[1].merge(TR[i],TR[1]);
    303     //这里全部都要注意运算顺序!
    304     if(!pl)return TR[1].ans;
    305     else if(!pr)return TL[1].ans;  //特判答案仅在lca一侧的情况
    306     else{
    307         for(ri i=0;i<10;i++){
    308             swap(TL[1].p0[i],TL[1].s0[i]);
    309             swap(TL[1].p1[i],TL[1].s1[i]);
    310         }  //“翻转”TL[1]
    311         
    312         TL[1].merge(TL[1],TR[1]);
    313         return TL[1].ans;
    314     }
    315 }
  • 相关阅读:
    Unity 向量点乘、叉乘
    为什么叫Unity3d为脚本语言
    Unity 围绕X、Y、Z旋转图例
    Kafka系列三之单节点多Broker部署
    Debezium SQL Server Source Connector+Kafka+Spark+MySQL 实时数据处理
    Debezium SQL Server Source Connector+Kafka+Spark+MySQL 实时数据处理
    Kudu遇到的坑,是真的坑~
    MySQL binlog浅析
    Kudu单机安装 【很简单】
    mybatis-plus坑之insert方法
  • 原文地址:https://www.cnblogs.com/running-coder-wfh/p/8242103.html
Copyright © 2011-2022 走看看