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

来源:互联网 发布:南风知我意2全文下载 编辑:程序博客网 时间:2024/05/18 02:09

先来说一下傻逼的做法。
考虑如何约束等差数列这个条件,如果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,rvoid 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!

0 0
原创粉丝点击