lcrtest的博客

一个SC蒟蒻的blog

bzoj 4028 [HEOI2015]公约数数列


首先分块
我们预处理每一块的前缀gcd和前缀xor
假设这一块开头为l,结尾为r
g[i]=gcd(a[l],a[l+1],a[i]),x[i]=xor(a[l],a[l+1],a[i])
然后把每一块按x[i]为第一关键字,下标为第二关键字排序
每次查询,扫描每一块,如果前缀gcd在这一块有变化,就暴力扫这一整块
如果前缀gcd没有变化,那么
(要查找的xor)=(m)(xor)/(gcd)
这个就在排序好的数组里面二分
修改就暴力重构这一块就行
由于gcd最多变化log次,每次变化nlog扫一遍
所以总的复杂度:O(Qnlog2)
清华大爷有一种更优越的方法:每次遇到gcd变化,就在这个块里面二分,找到这个转折点,这样一共只有log个转折点,每次二分查找在log的时间内找到这个点
这样可以在O(Qnlog)完成此题,可惜这样写反而更慢
O(Qnlog2)的代码:

#include<bits/stdc++.h>
#define N 100010
#define LL long long
using namespace std;
template<typename T>void read(T&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
struct Q{
    int id;LL v;
    bool operator<(const Q&a)const{
        if(v!=a.v)return v<a.v;
        else return id<a.id;
    }
}s[N];
LL gcd(LL x,LL y){
    return y==0?x:gcd(y,x%y);
}
int l[N],r[N],P,bel[N],tot,n,m;
LL x[N],g[N],a[N];
char op[55];
int find(int L,int R,LL v){
    int l=L,r=R;
    while(l^r){
        int mid=l+r>>1;
        if(s[mid].v<v)l=mid+1;
        else r=mid;
    }
    return s[l].v==v?s[l].id:-1;
}
void rebuild(int ps){
    g[l[ps]]=x[l[ps]]=a[l[ps]];s[l[ps]]=(Q){l[ps],x[l[ps]]};
    for(int i=l[ps]+1;i<=r[ps];i++){
        x[i]=x[i-1]^a[i];g[i]=gcd(a[i],g[i-1]);
        s[i]=(Q){i,x[i]};   
    }
    sort(s+l[ps],s+r[ps]+1);
}
int main(){
    read(n);P=sqrt(n)+1;
    for(int i=1;i<=n;i++){
        read(a[i]);
        bel[i]=(i-1)/P+1;r[bel[i]]=i;
        if(bel[i]>tot)tot++,l[bel[i]]=i;
    }
    r[bel[n]]=n;
    for(int i=1;i<=tot;i++)rebuild(i);
    read(m);
    for(;m--;){
        scanf("%s",op+1);
        if(op[1]=='M'){
            int x,y;read(x),read(y);
            a[++x]=y;rebuild(bel[x]);
        }
        else{
            LL m,_x=0,_g=a[1];bool f=0;
            read(m);
            for(int i=1;i<=tot&&!f;i++){
                if(gcd(_g,g[r[i]])==_g){
                    if(!(m%_g)){
                        int xx=find(l[i],r[i],(m/_g)^_x);
                        if(~xx)printf("%d\n",xx-1),f=1;
                    }
                    _x^=x[r[i]],_g=gcd(_g,g[r[i]]);
                }
                else{
                    for(int j=l[i];j<=r[i];j++){
                        _g=gcd(_g,a[j]);
                        _x=_x^a[j];
                        if(_g*_x==m){
                            printf("%d\n",j-1),f=1;
                            break;
                        }
                    }
                }
            }
            if(!f)puts("no");
        }
    }
}
阅读更多
版权声明:233333333333333333333333333333333333333333 https://blog.csdn.net/lcrtest/article/details/51660345
文章标签: 算法 题解
想对作者说点什么? 我来说一句

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

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭