[bzoj4373]算术天才⑨与等差数列 解题报告

先来说一下傻逼的做法。
考虑如何约束等差数列这个条件,如果k=0,就是[最大值=最小值];否则就是区间中[相邻两数差的绝对值的gcd=k][(最大值-最小值)/(r-l)=k][区间中没有相同元素]。
gcd可以用线段树求,这个东西看起来是O(log2n)的,但是其实是O(logn)的,因为考虑一次修改,从下往上gcd必然是不增的,而且考虑欧几里得算法求gcd的时候,每一次运算必然会至少除2,所以一次修改的O(logn)次欧几里得算法是均摊O(logA)(A是权值最大值)的。
所以问题就在于怎么确定区间中没有相同元素。
显然,如果记每个位置下一个和它权值相同的位置在哪,那就转化成了求区间最小值的问题。而这个玩意儿其实就是求后继。本来想用主席树求,但是mle了。。所以就写了个平衡树。正好学一下treap。。
treap竟然可以不保存父指针,还是有点厉害的。。插入和删除感觉其实跟普通的二叉堆差不多。就是要善用引用。

膜拜一下大爷的做法。
注意到这玩意儿是个子串,所以这个东西是可以hash的。比如说我们可以把区间中的每个数平方相加,然后与我们需要的等差数列的每个数的平方和check。(为什么不直接相加呢?因为那样很容易被小数据卡跪。。)为了跑得更快,显然我们需要自然溢出,但是这样做的话需要除6,所以我们把它们整体乘6即可。

代码(线段树+平衡树):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cassert>
const int N=3e5+5,M=3e5+5;
int a[N];
int n;

void in(int &x){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}

struct TS{
    int ch[2];
    int key;
    pair<int,int> data;
}treap[N];
int root;
int rand_32(){
    return rand()<<17^rand()<<5^rand();
}
void out(TS node){
    printf("{ch[0]=%d,ch[1]=%d,data=<%d,%d>}\n",node.ch[0],node.ch[1],node.data.first,node.data.second);
}
void outdfs(int node){
    if(node){
        printf("treap[%d]=",node);
        out(treap[node]);
        outdfs(treap[node].ch[0]);
        outdfs(treap[node].ch[1]);
    }
}
void rot(int node,int fa,bool dir){
    treap[fa].ch[dir]=treap[node].ch[!dir];
    treap[node].ch[!dir]=fa;
}
void add(int &node,int x){
    if(node){
        bool dir;
        if(treap[node].data<treap[x].data){
            add(treap[node].ch[1],x);
            dir=1;
        }
        else{
            add(treap[node].ch[0],x);
            dir=0;
        }
        if(treap[node].key>treap[x].key){
            rot(x,node,dir);
            node=x;
        }
    }
    else node=x;
}
void del(int &node,int x){
    if(node==x)
        if(treap[node].ch[0]||treap[node].ch[1]){
            bool dir=treap[treap[node].ch[0]].key>treap[treap[node].ch[1]].key;
            rot(node=treap[node].ch[dir],x,dir);
            del(treap[node].ch[!dir],x);
        }
        else node=0;
    else
        if(treap[node].data<treap[x].data)del(treap[node].ch[1],x);
        else del(treap[node].ch[0],x);
}
int pred(int x){
    int ans=0;
    for(int node=root;node;)
        if(treap[node].data>=treap[x].data)node=treap[node].ch[0];
        else{
            if(treap[node].data.first==treap[x].data.first)ans=treap[node].data.second;
            node=treap[node].ch[1];
        }
    return ans;
}
int succ(int x){
    int ans=n+1;
    for(int node=root;node;)
        if(treap[node].data<=treap[x].data)node=treap[node].ch[1];
        else{
            if(treap[node].data.first==treap[x].data.first)ans=treap[node].data.second;
            node=treap[node].ch[0];
        }
    return ans;
}

struct SS{
    int min,max;
    int gcd;
    int minsucc;
}segt[N<<2];
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void out(SS node){
    printf("{min=%d,max=%d,gcd=%d,minsucc=%d}\n",node.min,node.max,node.gcd,node.minsucc);
}
SS pushup(SS ls,SS rs,int l,int mid,int r){
    return (SS){min(ls.min,rs.min),max(ls.max,rs.max),gcd(abs(a[mid+1]-a[mid]),gcd(ls.gcd,rs.gcd)),min(ls.minsucc,rs.minsucc)};
}
void build(int node,int l,int r){
    if(l==r){
        treap[l]=(TS){0,0,rand_32(),make_pair(a[l],l)};
        add(root,l);
        segt[node]=(SS){a[l],a[l],0,succ(l)};

        //outdfs(root);
        //puts("");
    }
    else{
        build(rson),build(lson);
        segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
    }
    //printf("[%d,%d]=",l,r);
    //out(segt[node]);
}
void update(int node,int l,int r,int x,int y){
    if(l==r){
        del(root,x);
        a[x]=y;
        treap[x]=(TS){0,0,rand_32(),make_pair(y,x)};
        add(root,x);
        segt[node]=(SS){y,y,0,succ(x)};
        return;
    }
    else{
        if(x<=l+r>>1)update(lson,x,y);
        else update(rson,x,y);
        segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
    }
}
void pupdate(int node,int l,int r,int x){
    if(l==r){
        segt[node].minsucc=succ(x);
        return;
    }
    else{
        if(x<=l+r>>1)pupdate(lson,x);
        else pupdate(rson,x);
        segt[node]=pushup(segt[node<<1],segt[node<<1|1],l,l+r>>1,r);
    }
}
SS query(int node,int l,int r,int L,int R){
    //printf("query(%)")
    if(l==L&&r==R)return segt[node];
    if(R<=l+r>>1)return query(lson,L,R);
    if(L>l+r>>1)return query(rson,L,R);
    return pushup(query(lson,L,l+r>>1),query(rson,(l+r>>1)+1,R),L,l+r>>1,R);
}
int main(){
    freopen("bzoj_4373.in","r",stdin);
    //freopen("bzoj_4373.out","w",stdout);
    treap[0].key=0x7fffffff;

    int m;
    in(n),in(m);
    for(int i=1;i<=n;++i)in(a[i]);
    build(1,1,n);

    int op;
    int x,y;
    int l,r,k;
    int yescnt=0;
    SS ans;
    int pre;
    while(m--){
        //printf("-----%d-----\n",m);
        in(op);
        if(op==1){
            in(x),in(y);
            x^=yescnt,y^=yescnt;

            //printf("Update:%d=%d\n",x,y);

            pre=pred(x);
            update(1,1,n,x,y);
            if(pre)pupdate(1,1,n,pre);
            if(pre=pred(x))pupdate(1,1,n,pre);
        }
        else{
            in(l),in(r),in(k);
            l^=yescnt,r^=yescnt,k^=yescnt;

            //printf("Query([%d,%d],%d)\n",l,r,k);

            ans=query(1,1,n,l,r);

            //printf("ans=");
            //out(ans);

            if(k?ans.gcd%k==0&&(ans.max-ans.min)/k==r-l&&ans.minsucc>r:ans.max==ans.min){
                puts("Yes");
                ++yescnt;
            }
            else puts("No");
        }
    }
}

代码(hash):

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=3e5+5,M=3e5+5;
char * cp=(char *)malloc(20000000);
void in(int &x){
    while(*cp<'0'||*cp>'9')++cp;
    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');
}

const int Z=1<<19;
struct ZS{
    int min;
    int sum;
}zkw[Z<<1];
void pushup(int node){
    zkw[node]=(ZS){min(zkw[node<<1].min,zkw[node<<1|1].min),zkw[node<<1].sum+zkw[node<<1|1].sum};
}

const int inf=0x7fffffff;
int main(){
    freopen("bzoj_4373.in","r",stdin);
    freopen("bzoj_4373_hash.out","w",stdout);
    fread(cp,1,20000000,stdin);
    int n,m;
    in(n),in(m);
    for(int i=1;i<=n;++i){
        in(zkw[Z+i].min);
        zkw[Z+i].sum=zkw[Z+i].min*zkw[Z+i].min*6;
    }
    for(int i=Z;--i;)pushup(i);
    int op;
    int x,y;
    int l,r,len,k;
    int yescnt=0;
    ZS ans;
    while(m--){
        in(op);
        if(op==1){
            in(x),in(y);

            x^=yescnt,y^=yescnt;

            zkw[x+=Z]=(ZS){y,y*y*6};
            while(x>>=1)pushup(x);
        }
        else{
            in(l),in(r),in(k);

            l^=yescnt,r^=yescnt,k^=yescnt;

            len=r-l;
            ans=(ZS){inf,0};
            for(l+=Z-1,r+=Z+1;r-l>1;l>>=1,r>>=1){
                if(~l&1){
                    //cout<<(l^1)-Z<<endl;
                    ans.min=min(ans.min,zkw[l^1].min);
                    ans.sum=ans.sum+zkw[l^1].sum;
                    //cout<<"Get\n";
                }
                if(r&1){
                    //cout<<(r^1)-Z<<endl;
                    ans.min=min(ans.min,zkw[r^1].min);
                    ans.sum=ans.sum+zkw[r^1].sum;
                    //cout<<"Get\n";
                }
            }
            //cout<<ans.min<<" "<<ans.sum<<endl;
            //cout<<(ans.min+ans.min+(r-l)*k)<<"*"<<(r-l+1)<<"->"<<((unsigned)((ans.min+ans.min+(r-l)*k)*(r-l+1))>>1)<<endl;
            //cout<<ans.min<<endl;
            //cout<<((unsigned)((ans.min+ans.min+len*k)*(len+1))>>1)<<endl;
            //cout<<(LL)ans.min*ans.min%Mod*(len+1)%Mod<<'+'<<(LL)len*(len+1)%Mod*ans.min%Mod*k%Mod<<'+'<<(LL)len*(len+1)%Mod*(len<<1|1)%Mod*pow(6,Mod-2)%Mod*k%Mod*k%Mod<<endl;
            if(ans.sum==ans.min*ans.min*(len+1)*6+len*(len+1)*ans.min*k*6+len*(len+1)*(len<<1|1)*k*k){
                puts("Yes");
                ++yescnt;
            }
            else puts("No");
        }
    }
}

总结:
①用引用写treap可以省去很多讨论。
②treap是可以不保存父指针的,这意味着一棵普通的treap也是可以可持久化的。(其实splay也是可以不保存父指针的,但是因为splay的时间复杂度是均摊的,所以它并不能可持久化。)
③判断两个子串是否相等?——hash!

发布了188 篇原创文章 · 获赞 93 · 访问量 22万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览