• 【icpc2019网络赛南昌站】Yukino With Subinterval


    傻b错误调一天系列

    原题:

    大意:给你一个数列a,字词两种操作:

    1.把c[l]改成r

    2.询问在区间[l,r]中,有多少个极大子区间满足子区间里的数全部一样,且在[x,y]范围内

    (对于满足条件的区间A,若不存在满足条件的区间B使得A包含于B,则称A为极大子区间)

    序列问题,要求复杂度O(nlogn),联想cdq分治

    值域可以容斥拆成[1,l-1]和[1,r]两个询问,即把询问转化为区间中数小于等于x的数有多少个

    可以把初始数列看成0,然后用修改操作代替初始数列

    那么现在就存在偏序:若修改A的时间小于询问B,且A的值小于B的值,则A可以给B提供贡献

    对于计算贡献,我们建一个线段树,字词单点修改,并查询有多少个不同的非0极大子区间,这个比较好写

    初始按时间排序,然后按值域分治

    然后这题就做完了马

    反例:

    6 3
    1 1 4 5 1 4
    2 1 6 2 5
    1 6 1
    2 1 6 2 5

    上面的做法会输出3 3,正确答案却是3 2

    考试的时候我写这个做法然后深度自闭

    出错的原因是因为我们按值域分治,那么当第6个数(被视为操作1 6 4)和第三个操作(2 1 6 2 5)分到一个分治区间时,因为操作2(1 6 1)被分到左边的分治区间了,所以不会把代表第6个数的操作覆盖掉,这时第6个数就给第3个操作产生了贡献(尽管第三个操作进行时它已经被覆盖掉了)

    更换提供贡献的顺序是不行的,这是个死循环,不管是用前序、中序或后序cdq分治都无法解决,根本原因是操作2(1 6 1)实际上给操作3(2 1 6 2 5)一个负贡献(它把一个本来合法的区间变得不合法),却因为值域没有达到操作3的范围([2,5])而被忽略了

    这个负贡献在分治过程中无法统计(至少我没找到方法)

    正确做法是

    只考虑一个区间中的左端点那个数,新建一个数组b,对于每个b[i],若a[i]==a[i-1]则b[i]为0否则为a[i]

    问题就转化为求区间[l+1,r]中有多少个数,最后单独特判a[l]是否在区间[x,y]内

    这个就是经典cdq分治问题,由于数的值域为[1,n],所以用权值线段树(树状数组)可以方便地统计贡献

    这种做法和上一种的根本区别在于一个修改操作是覆盖,而另一个是增添和删除,在分治中第二种统计贡献比第一种简单直接很多

    然后我就迎来了这题第二次自闭,在晚上10:30写出正确代码后找bug到11:30,直至次日下午5:00才发现问题

    只因为一句话的位置:

    这是对操作1的处理,注释代码为原位置

    如果把d[i]赋值写在后边,就会导致当r==c[l]时,d[i]={0,0,0,0,0,0,0},而我在判断是否应该输出的时候是靠d[i].mk是否为1来判断的

    这就导致当r==c[l]发生时,会有修改操作被当成查询操作多输出一个数,自然会WA

    这个bug我花了这么长时间没找出来,一个很重要的原因时总在原地打转,算法基本框架检查过很多遍没有问题了,就应该去思考一些细节,尤其是看起来很奇怪很特殊的部分

    我倒是看了细节,但是偏偏没有注意到这么奇怪而特殊的一个特判

    这个故事也告诉我们有时候睡一觉休息一下bug就自己出来了,不能盲目死磕

    代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 using namespace std;
     7 int rd(){int z=0,mk=1;  char ch=getchar();
     8     while(ch<'0'||ch>'9'){if(ch=='-')mk=-1;  ch=getchar();}
     9     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
    10     return z*mk;
    11 }
    12 struct nds{int mk,x,y,z,id,ans;}a[810000];  int atp=0;
    13 int n,m;
    14 int b[210000],c[210000];
    15 nds q[810000];
    16 int v[810000];
    17 int ans[210000];
    18 nds d[210000];
    19 inline int lbt(int x){  return x&-x;}
    20 void mdf(int x,int y){  for(;x<=n;x+=lbt(x))  v[x]+=y;}
    21 int qry(int x){  int bwl=0;  for(;x;x-=lbt(x))  bwl+=v[x];  return bwl;}
    22 void cdq(int x,int y,int l,int r){
    23     if(x>=y)  return ;
    24     if(l>r)  return ;
    25     if(l==r){
    26         for(int i=x;i<=y;++i){
    27             if(!a[i].mk)  mdf(a[i].y,a[i].z);
    28             else  a[i].ans+=qry(a[i].z)-qry(a[i].y-1);
    29         }
    30         for(int i=x;i<=y;++i)if(!a[i].mk)  mdf(a[i].y,-a[i].z);
    31         return ;
    32     }
    33     int md=(l+r)>>1;
    34     int cnt1=0;
    35     for(int i=x;i<=y;++i){
    36         if(a[i].x<=md)  ++cnt1;
    37         if(!a[i].mk && a[i].x<=md)  mdf(a[i].y,a[i].z);
    38         if(a[i].mk && a[i].x>md)  a[i].ans+=qry(a[i].z)-qry(a[i].y-1);
    39     }
    40     for(int i=x;i<=y;++i)if(!a[i].mk && a[i].x<=md)
    41         mdf(a[i].y,-a[i].z);
    42     int hd1=x,hd2=x+cnt1;
    43     for(int i=x;i<=y;++i){
    44         if(a[i].x<=md)  q[hd1++]=a[i];
    45         else  q[hd2++]=a[i];
    46     }
    47     for(int i=x;i<=y;++i)  a[i]=q[i];
    48     cdq(x,hd1-1,l,md),cdq(hd1,y,md+1,r);
    49 }
    50 void prvs(){
    51     atp=0;
    52     for(int i=1;i<=m;++i)  ans[i]=0;
    53 }
    54 int main(){
    55     //freopen("ddd.in","r",stdin);
    56     cin>>n>>m;
    57     prvs();
    58     for(int i=1;i<=n;++i)  c[i]=rd();
    59     c[0]=0,c[n+1]=0;
    60     for(int i=1;i<=n;++i){
    61         if(c[i]!=c[i-1])  a[++atp]=(nds){0,i,c[i],1,-1,0};
    62         b[i]=c[i];
    63     }
    64     int mk,l,r,ql,qr;
    65     for(int i=1;i<=m;++i){
    66         mk=rd();
    67         if(mk==1){
    68             l=rd(),r=rd();
    69             d[i]=(nds){mk,l,r,0,0,i};
    70             if(r==c[l])  continue;
    71             if(c[l]!=c[l-1])  a[++atp]=(nds){0,l,c[l],-1,i,0};
    72             if(r!=c[l-1])  a[++atp]=(nds){0,l,r,1,i,0};
    73             if(l<n){
    74                   if(r==c[l+1])  a[++atp]=(nds){0,l+1,c[l+1],-1,i,0};
    75                 if(c[l]==c[l+1])  a[++atp]=(nds){0,l+1,c[l+1],1,i,0};
    76             }
    77             c[l]=r;
    78             //d[i]=(nds){mk,l,r,0,0,i};  Attention!!!
    79         }
    80         else{
    81             l=rd(),r=rd(),ql=rd(),qr=rd();
    82             a[++atp]=(nds){1,r,ql,qr,i,0};
    83             a[++atp]=(nds){-1,l,ql,qr,i,0};
    84             d[i]=(nds){mk,l,r,ql,qr,i};
    85         }
    86     }
    87     cdq(1,atp,1,n);
    88     for(int i=1;i<=atp;++i)if(a[i].mk)  ans[a[i].id]+=a[i].mk*a[i].ans;
    89     for(int i=1;i<=m;++i){
    90         if(d[i].mk==1)  b[d[i].x]=d[i].y;
    91         else  printf("%d
    ",ans[i]+(b[d[i].x]>=d[i].z && b[d[i].x]<=d[i].id));
    92     }
    93     return 0;
    94 }
    View Code
  • 相关阅读:
    【luogu P7418】Counting Graphs P(DP)(思维)(容斥)
    【luogu P7417】Minimizing Edges P(贪心)(思维)
    多边形序列(组合数)(高精)(NTT)
    【luogu P3803】【模板】多项式乘法(NTT)
    【luogu P1919】【模板】A*B Problem升级版(FFT快速傅里叶)
    【luogu P6139】【模板】广义后缀自动机(广义 SAM)
    【luogu P7529】Permutation G(几何)(数学)(DP)
    【luogu P5787】graph / 二分图 /【模板】线段树分治(扩展域并查集)(线段树分治)
    同桌的你(环套树)(DP)
    石子游戏(博弈论)(Spaly)
  • 原文地址:https://www.cnblogs.com/cdcq/p/11498957.html
Copyright © 2020-2023  润新知