[HEOI2015] 公约数数列

题目描述:

eNQLr.md.png

题目分析:

分块,块内维护 前缀gcd 前缀Xor
修改暴力重构
查询时若gcd(gcd(块尾,pregcd))==pregcd,则说明本块内的所有数都不影响gcd 二分查找符合要求的Xor
否则暴力查询

题目链接:

Luogu 4108
BZOJ 4028

Ac 代码:

// luogu-judger-enable-o2
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define il inline 
#define ll long long
const int maxm=110000;
struct node{
    int id;
    ll pxor;
}a[maxm];
int block_len,block_num;
int n,q,lx[maxm],rx[maxm];
ll val[maxm],pos[maxm],pg[maxm],xr[maxm];
inline bool comp(node x,node y)
{
    return (x.pxor==y.pxor?x.id<y.id:x.pxor<y.pxor);
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
il void work(int i)
{
   pg[lx[i]]=a[lx[i]].pxor=xr[lx[i]]=val[lx[i]];
   pos[lx[i]]=i;
   a[lx[i]].id=lx[i];
   for(int j=lx[i]+1;j<=rx[i];j++)
   {
     pos[j]=i,a[j].id=j;
     pg[j]=gcd(pg[j-1],val[j]);
     xr[j]=xr[j-1]^val[j];
     a[j].pxor=xr[j];
   }
   std::sort(a+lx[i],a+rx[i]+1,comp);
}
il void init()
{
   block_len=std::sqrt(n);
   block_num=n/block_len+((n%block_len)!=0);
   for(int i=1;i<=block_num;i++)
   {
     lx[i]=(i-1)*block_len+1,rx[i]=std::min(i*block_len,n);
     work(i);
   }
}
il int BS(ll x,int l,int r)
{
    int ans=l;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(a[mid].pxor>=x) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
il void getans()
{
    ll x;
    scanf("%lld",&x);
    ll g=val[1],nxr=0;
    for(int i=1;i<=block_num;i++)
    {
        int s=lx[i],t=rx[i];
        if(gcd(pg[t],g)==g)
        {
           if(x%g==0)
           {
             int ans=(x/g)^nxr;
             int anspos=BS(ans,s,t);
             if(a[anspos].pxor==ans)
             {
                printf("%d\n",a[anspos].id-1);
                return;
             }
           }
           g=gcd(g,pg[t]),nxr=nxr^xr[t];
        }
        else
        {
           for(int j=s;j<=t;j++)
           {
             g=gcd(g,val[j]),nxr=nxr^val[j];
             if(g*nxr==x)
             {
                printf("%d\n",j-1);
                return;
             }
           }
        }
    }
    printf("no\n");
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
     scanf("%lld",&val[i]);
    init();
    scanf("%d",&q);
    for(int i=1,poi;i<=q;i++)
    {
        char s[10];
        ll x;
        scanf("%s",s);
        if(s[0]=='M')
        {
            scanf("%d%lld",&poi,&x);
            poi++; 
            val[poi]=x;
            work(pos[poi]);
        }
        else getans();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值