BZOJ4028: [HEOI2015]公约数数列

BZOJ4028

看着这么神的题,最后发现是个“聪明人”打的暴力。。
首先进行分块,维护每一块内的 GcdXor ,每个块内存下二元组 x,Xor 表示位置和当前位置的亦或值,按照亦或值为第一关键字排序。
对于一次修改就将块内信息重新维护,每次 O(nlogn)
对于一次询问,记 LastGcd,LastXor 分别表示之前的 GcdXor
每个块依次查询,如果 gcd(LastGcd,Gcdi)==LastGcd 。也就表明取到当前块的任意一个位置,总的 gcd 都是 LastGcd 。那么就是在块中查找是否存在一个 Xor 值为 x/LastGcd 。这个在排序好的数组中二分就可以了。复杂度是 O(logn)
其它情况下就暴力查询。每次 O(n)

为什么这样做复杂度有保障呢!!?
如果 gcd(LastGcd,Gcdi)=LastGcd 。那么 LastGcd 就会至少缩小两倍。
那么暴力查询最多只会做 O(logmax(ai)) ,然后 ai<=109 。所以一次 Query 最多只会做30次暴力查询。
复杂度大概是 O(N(N+logN)+QNlogN)

【代码】

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100005
using namespace std;
typedef long long ll;
typedef pair<ll,int> pa;

ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,block,tot;
int bl[N],Num[320],L[320],R[320];
ll a[N],Xor[320],Gcd[320];
bool Flag[320];

class Hash{
    public:
        int id;ll x;
}hash[N];

bool operator <(Hash A,Hash B) {
    return A.x<B.x||(A.x==B.x&&A.id<B.id);
}

ll _Gcd(ll a,ll b){
    return b==0?a:_Gcd(b,a%b);
}

void Rebuild(int x)
{
    Gcd[x]=a[L[x]];Xor[x]=Flag[x]=0;
    for(register int i=L[x];i<=R[x];i++) {
        Gcd[x]=_Gcd(Gcd[x],a[i]);
        Xor[x]^=a[i];hash[i].x=Xor[x],hash[i].id=i;
    }
    sort(hash+L[x],hash+R[x]+1);
}

void Input_Init()
{
    n=read();block=sqrt(n);
    for(register int i=1;i<=n;i++) {
        a[i]=read();bl[i]=(i-1)/block+1;
        if(bl[i]!=bl[i-1]) L[bl[i]]=i;
        if(i%block==0||i==n) R[bl[i]]=i;
    }
    tot=bl[n];
    for(register int i=1;i<=tot;i++) Rebuild(i);
}

void Modify()
{
    int x;ll y;
    x=read()+1,y=read();a[x]=y;Flag[bl[x]]=1;
}

int Find(int L,int R,ll y)
{
    int rtn=0;
    while(L<=R)
    {
        int mid=L+R>>1;
        if(hash[mid].x>=y) rtn=mid,R=mid-1;
        else L=mid+1;
    }
    if(hash[rtn].x!=y) return 0;
    return hash[rtn].id;
}

int Brute_Force(int x,ll LastGcd,ll LastXor,ll y)
{
    for(register int i=L[x];i<=R[x];i++)
    {
        LastGcd=_Gcd(LastGcd,a[i]);
        LastXor^=a[i];
        if(LastGcd*LastXor==y) return i;
    }
    return 0;
}

int Query()
{
    ll LastXor=0,LastGcd=a[1],x=read();
    for(register int i=1;i<=tot;i++)
    {
        if(Flag[i]) Rebuild(i);
        LastGcd=__gcd(LastGcd,Gcd[i-1]);
        LastXor^=Xor[i-1];
        if(_Gcd(LastGcd,Gcd[i])==LastGcd) {
            if(x%LastGcd!=0) continue;
            int t=Find(L[i],R[i],LastXor^x/LastGcd);
            if(t) return t;
        }
        else {
            int t=Brute_Force(i,LastGcd,LastXor,x);
            if(t) return t;
        }
    }
    return -1;
}

void Solve()
{
    m=read();
    while(m--)
    {
        char ch[10];scanf("%s",ch);
        if(ch[0]=='M') Modify();
        else {
            int t=Query();
            if(t==-1) printf("no\n");
            else printf("%d\n",t-1);
        }
    }
}

int main()
{
    Input_Init();
    Solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值