BZOJ4028: [HEOI2015]公约数数列

BZOJ4028: [HEOI2015]公约数数列

Description

设计一个数据结构. 给定一个正整数数列 a_0, a_1, ..., a_{n - 1},你需要支持以下两种操作:

1. MODIFY id x: 将 a_{id} 修改为 x.
2. QUERY x: 求最小的整数 p (0 <= p < n),使得 gcd(a_0, a_1, ..., a_p) * XOR(a_0, a_1, ..., a_p) = x. 其中 XOR(a_0, a_1, ..., a_p) 代表 a_0, a_1, ..., a_p 的异或和,gcd表示最大公约数。

Input

 输入数据的第一行包含一个正整数 n.

接下来一行包含 n 个正整数 a_0, a_1, ..., a_{n - 1}.
之后一行包含一个正整数 q,表示询问的个数。
之后 q 行,每行包含一个询问。格式如题目中所述。

Output

对于每个 QUERY 询问,在单独的一行中输出结果。如果不存在这样的 p,输出 no.

Sample Input

10
1353600 5821200 10752000 1670400 3729600 6844320 12544000 117600 59400 640
10
MODIFY 7 20321280
QUERY 162343680
QUERY 1832232960000
MODIFY 0 92160
QUERY 1234567
QUERY 3989856000
QUERY 833018560
MODIFY 3 8600
MODIFY 5 5306112
QUERY 148900352

Sample Output

6
0
no
2
8
8

HINT

 对于 100% 的数据,n <= 100000,q <= 10000,a_i <= 10^9 (0 <= i < n),QUERY x 中的 x <= 10^18,MODIFY id x 中的 0 <= id < n,1 <= x <= 10^9.


题解Here!

 

本来想用线段树维护一下,然后发现我好像需要$3$个$\log_2n$,这是要爆炸的节奏啊。。。

所以我们拿出了分块。

一开始我还在想,$GCD$和$XOR$有什么联系,然后发现,这不需要联系啊。。。

首先,$GCD$有个还算不错的性质:

对于一列数组,从左往右取前缀$gcd$,不同的值最多只有$\log_2n$种。

并且每次值如果改变,那么前缀$gcd$的值至少除以二。

对于每个块,维护下列信息:

块内数据$xor$和,块内$gcd$,块的头尾两个数的前缀$gcd$,块内每个数以块左端点为头的前缀$xor$和。

对于第四类信息,还需要用某种方法,使得支持在$\log_2n$的时间内询问是否存在一个数。

修改的时候,修改位置所在块暴力重构,后面的块更新第三类信息即可。

查询的时候,如果某个块的第三类信息相等,说明这个块内前缀$gcd$都不变,有没有解查表就知道了。

这个表怎么搞呢?

假设暴力扫描,如果前面的块所取到的前缀$gcd$为$lastgcd$,$xor$为$lastxor$。

若$gcd(lastgcd,Gcd[r[i]])==lastgcd$,则说明这个块内所有的数取$gcd$后都是$lastgcd$,那么$xor[j]=(\frac{x}{lastgcd}\quad xor \quad lastxor)$。

然后在另一个排好序的数组中二分查找就可以了。

否则,这个块内暴力访问看一下是否有解,因为不同的$gcd$值不超过$\log_2n$种,所以暴力访问次数并不多。

所以复杂度就是$O(n\sqrt n\log_2n)$。

附代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#define MAXN 100010
using namespace std;
int n,m,block;
int colour[MAXN],Left[MAXN],Right[MAXN];
long long val[MAXN],gcd_sum[MAXN],xor_sum[MAXN];
struct node{
    long long x;
    int id;
    friend bool operator <(const node &p,const node &q){
        if(p.x==q.x)return p.id<q.id;
        return p.x<q.x;
    }
}a[MAXN];
inline long long read(){
    long long date=0,w=1;char c=0;
    while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    return date*w;
}
long long gcd(long long x,long long y){
    if(!y)return x;
    return gcd(y,x%y);
}
int half_find(int l,int r,long long x){
    int mid,ans=l;
    while(l<=r){
        mid=l+r>>1;
        if(a[mid].x>=x){ans=mid;r=mid-1;}
        else l=mid+1;
    }
    return ans;
}
void build(int x){
    gcd_sum[Left[x]]=xor_sum[Left[x]]=val[Left[x]];
    a[Left[x]]=(node){val[Left[x]],Left[x]};
    for(int i=Left[x]+1;i<=Right[x];i++){
        gcd_sum[i]=gcd(gcd_sum[i-1],val[i]);
        xor_sum[i]=xor_sum[i-1]^val[i];
        a[i]=(node){xor_sum[i],i};
    }
    sort(a+Left[x],a+Right[x]+1);
}
int solve(long long x){
    int ans=-1;
    long long Gcd=val[1],Xor=0;
    for(int i=1;i<=colour[n]&&ans==-1;i++){
        if(gcd(Gcd,gcd_sum[Right[i]])==Gcd){
            if(x%Gcd==0){
                long long k=(x/Gcd)^Xor;
                int pos=half_find(Left[i],Right[i],k);
                if(a[pos].x==k){
                    ans=a[pos].id;
                    break;
                }
            }
            Gcd=gcd(Gcd,gcd_sum[Right[i]]);Xor^=xor_sum[Right[i]];
        }
        else{
            for(int j=Left[i];j<=Right[i];j++){
                Gcd=gcd(Gcd,val[j]);Xor^=val[j];
                if(Gcd*Xor==x){
                    ans=j;
                    break;
                }
            }
            if(ans!=-1)break;
        }
    }
    return ans;
}
void work(){
    char ch[10];
    long long x,y;
    while(m--){
        scanf("%s",ch);x=read();
        if(ch[0]=='M'){
            x++;y=read();
            val[x]=y;
            build(colour[x]);
        }
        else{
            int s=solve(x);
            if(s==-1)printf("no\n");
            else printf("%d\n",s-1);
        }
    }
}
void init(){
    n=read();
    block=(int)sqrt(n);
    for(int i=1;i<=n;i++){
        colour[i]=(i-1)/block+1;
        if(!Left[colour[i]])Left[colour[i]]=i;
        Right[colour[i]]=i;
        val[i]=read();
    }
    for(int i=1;i<=colour[n];i++)build(i);
    m=read();
}
int main(){
    init();
    work();
    return 0;
}

 

posted @   符拉迪沃斯托克  阅读(344)  评论(1编辑  收藏  举报
Live2D
欢迎阅读『BZOJ4028: [HEOI2015]公约数数列』
点击右上角即可分享
微信分享提示