BZOJ4028 [HEOI2015]公约数数列 分块

给定一个数列,要求资磁以下两种操作:
1.单点修改.
2.求数列中最前的位置p,使前缀最大公约数gcd*前缀异或和xor==一个输入的数x.

考虑分块+暴力.
按照 n 分块,求出每一块的前缀gcd和前缀xor.
单点修改时对所在块暴力更新gcd与xor
每次更新的复杂度是 nlogn .

查询时分两种情况

①如果GCD(gcd_now,gcd_pre)==gcd_pre,即到当前块尾gcd没有改变.
则当前块前缀gcd唯一,所以符合的前缀xor值也是唯一的。
在这一块中二分查找是否存在即可.

②如果gcd不相等,那么对这一块从头到尾暴力扫描答案.
但是由于gcd最多改变 logn 次,所以这里复杂度也是 nlogn 的.

总复杂度不超过 O(nnlogn) .

实现起来跟讲的一样复杂。。想的时候没想到查询①…
上代码

#include<bits/stdc++.h>
#define LL long long
#define clr(x,i) memset(x,i,sizeof(x))
using namespace std;
const int N=100005;
inline void read(LL &x)
{
    x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
}
struct data{
    LL xt,id;
    friend bool operator <(data a,data b){
        return a.xt==b.xt ? a.id<b.id : a.xt<b.xt;
    }
}b[N];
LL n,q,a[N],gs[N],xs[N];
int id[N],l[666],r[666],tim,tot;

LL GCD(LL a,LL b)
{
    if(!b)return a;
    return GCD(b,a%b);
}
void work(int x)
{
    int be=l[x],en=r[x];
    gs[be]=xs[be]=b[be].xt=a[be];
    b[be].id=be;
    for(int i=be+1;i<=en;i++){
        gs[i]=GCD(gs[i-1],a[i]);
        xs[i]=xs[i-1]^a[i];
        b[i].xt=xs[i];b[i].id=i;
    }
    sort(b+be,b+en+1);
}
int find(LL x,int l,int r)
{
    int mid,ans=l;
    while(l<=r){
        mid=(l+r)>>1;
        if(b[mid].xt>=x)
          ans=mid,r=mid-1;
        else
          l=mid+1;
    }
    return ans;
}
int main()
{
    read(n);
    tim=sqrt((double)n);tot=(int)(n-1)/tim+1;
    //printf("%d %d\n",tim,tot);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<=n;i++){
        id[i]=(i-1)/tim+1;
        if(!l[id[i]])l[id[i]]=i;
        r[id[i]]=i;
    }
    for(int i=1;i<=tot;i++)
      work(i);
    read(q);
    while(q--)
    {
        char op[10];LL x,y;
        scanf("%s",op);
        if(op[0]=='M'){
            read(x);read(y);x++;
            a[x]=y;
            work(id[x]);
        }
        else{
            read(x);
            LL gl=a[1],xl=0,flag=0;
            for(int i=1;i<=tot&&!flag;i++)
            {
                int be=l[i],en=r[i];
                if(GCD(gs[en],gl)==gl)
                {
                    if(x%gl==0){
                        LL xans=(x/gl)^xl;
                        int pos=find(xans,be,en);
                        if(b[pos].xt==xans){
                            printf("%d\n",b[pos].id-1);flag=1;break;
                        }
                        //
                    }
                    xl^=xs[en];//gl=GCD(gl,gs[en]);
                }
                else{
                    for(int j=be;j<=en;j++)
                    {
                        gl=GCD(gl,a[j]);xl^=a[j];
                        if(gl*xl==x){
                            printf("%d\n",j-1);flag=1;break;
                        }
                    }
                    if(flag)break;
                }

            }
            if(!flag)printf("no\n");
        }
    }
    return 0;
}

之前公式挂了,重发一遍

猜你喜欢

转载自blog.csdn.net/wolf_reiser/article/details/78941351
今日推荐