gcd的性质+分块 Bzoj 4028

4028: [HEOI2015]公约数数列

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 865  Solved: 311
[Submit][Status][Discuss]

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.

思路:

因为我们知道,gcd每次必然是/2的,所以gcd最多就只要log个,然后呢,我们对每个块都分块,并且记录每个块的gcd[i]和xor[i],分别表示gcd(1~i)和xor(1~i),

①如果是单点修改的话,就暴力更新一下目前的块即可,所以暴力更新的复杂度为n^0.5

②如说是查询的话,我们就暴力每个块,对于目前这个块。

定义pregcd表示目前这个块之前所有的数字的gcd,prexor为目前这个块之前所有的数字的xor。然后,如果这个块中,他的gcd[r[i]]和pregcd求gcd以后没有发生变化,那么就表示可能存在xor[j]^prexor * (pregcd和gcd[r[i]]的gcd) = val。那么我们就二分去看看存不存在这个xor[j],如果存在,就从头开始暴力,找到最小的。当然,这个二分的话可以用set来维护,只需要set.count()就可以查询了。所以这里的复杂度为sqrt(n) * log(sqrt(n))

如果gcd发生了变化那么我们就暴力去查询即可,所以这里的复杂度为sqrt(n).

因为gcd最多不会超过log个,所以上面查询+修改的最大复杂度为q*sqrt(n)*log(sqrt(n))

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
/*
首先,我们对所有的东西进行分块,然后我们对每个区间进行处理,分别得到这个区间的gcd和xor。
①然后如果前面的区间到这个块以后的gcd如果发生了改变,那么,我们就暴力这个块,复杂度为sqrt(n)
②如果前面的区间到这个块以后gcd没有发生改变,那么我们就二分这个块,二分的证明如下:
假定lastxor是该区间之前的xor值,lastgcd是该区间之前的gcd的值,假定我们要寻找的xor[j]是在这个块中
那么xor[j]^lastxor * lastgcd = k,转化以后为xor[j] = k/lastgcd ^ lastxor,所以我们只要二分这个块就好了
因此这里的复杂度为logn
因为gcd一共就只有logn个,所以复杂度最坏为sqrt(n) * logn个
*/
const int maxn = 1e6 + 5;
LL a[maxn], Xor[maxn], gcd[maxn];
int l[maxn], r[maxn], block, num, belong[maxn];
int n, q;
set<LL> S[maxn];

LL get_gcd(LL a, LL b){
    return b == 0 ? a : get_gcd(b, a % b);
}

void build(){
    block = sqrt(n); num = n / block;
    if (n % block) num++;
    for (int i = 1; i <= num; i++)
        l[i] = (i - 1) * block + 1, r[i] = i * block;
    r[num] = n;
    for (int i = 1; i <= n; i++){
        belong[i] = (i - 1) / block + 1;
    }
}

void update(int p){
    S[p].clear();
    gcd[l[p]] = a[l[p]], Xor[l[p]] = a[l[p]];
    S[p].insert(Xor[l[p]]);
    for (int i = l[p] + 1; i <= r[p]; i++){
        gcd[i] = get_gcd(gcd[i - 1], a[i]);
        Xor[i] = Xor[i - 1] ^ a[i];
        S[p].insert(Xor[i]);
    }
}

void query(LL val){
    LL prexor, pregcd;
    for (int i = 1; i <= r[1]; i++){
        if (gcd[i] * Xor[i] == val){
            printf("%d\n", i-1); return ;
        }
    }
    prexor = Xor[r[1]], pregcd = gcd[r[1]];
    for (int i = 2; i <= num; i++){
        LL nowgcd = get_gcd(pregcd, gcd[r[i]]);
        if (nowgcd == pregcd){///xor[i] ^ prexor * nowgcd = val
            LL tmp = val / nowgcd ^ prexor;
            if (val % nowgcd == 0 && S[i].count(tmp)){
                for (int j = l[i]; j <= r[i]; j++){
                    if (Xor[j] == tmp){
                        printf("%d\n", j - 1); return ;
                    }
                }
            }
        }
        else {
            for (int j = l[i]; j <= r[i]; j++){
                nowgcd = get_gcd(gcd[j], pregcd);
                LL tmp = val / nowgcd ^ prexor;
                if (val % nowgcd == 0 && Xor[j] == tmp){
                    printf("%d\n", j - 1); return;
                }
            }
        }
        pregcd = get_gcd(pregcd, gcd[r[i]]);
        prexor = prexor ^ Xor[r[i]];
    }
    printf("no\n");
}

int main(){
    cin >> n;
    for (int i = 1; i <= n; i++){
        scanf("%lld", a + i);
    }
    build();
    for (int i = 1; i <= num; i++)
        update(i);
    scanf("%d", &q);
    char ch[20];
    for (int i = 1; i <= q; i++){
        scanf("%s", ch);
        if (ch[0] == ‘M‘){
            int p; LL val;
            scanf("%d%lld", &p, &val);
            a[++p] = val;
            update(belong[p]);
        }
        else{
            LL val;
            scanf("%lld", &val);
            query(val);
        }
    }
    return 0;
}

时间: 2024-10-25 15:01:20

gcd的性质+分块 Bzoj 4028的相关文章

【BZOJ 2820】 YY的GCD (莫比乌斯+分块)

YY的GCD Description 神犇YY虐完数论后给傻×kAc出了一题 给定N, M,求1<=x<=N, 1<=y<=M且gcd(x, y)为质数的(x, y)有多少对 kAc这种傻×必然不会了,于是向你来请教…… 多组输入 Input 第一行一个整数T 表述数据组数 接下来T行,每行两个正整数,表示N, M Output T行,每行一个整数表示第i组数据的结果 Sample Input 210 10100 100 Sample Output 302791 Hint T =

bzoj 4028 : [HEOI2015]公约数数列

之前看了好几次都没什么思路,今天下定决心把这题切了. 观察到$0-x$的gcd最多变化log次,因为它每次变化一定至少要去掉一个质因子,所以我们可以枚举gcd. 因为数据范围比较小,所以想到了分块. 设T为块的大小. 维护块首到块里每个位置的gcd和xor,再把xor排序. 修改的时候暴力改就行,复杂度$TlogT$. 询问的时候如果gcd在这个块里变化了,就把这个块暴力扫一遍,否则说明gcd在这个块里不变,相当于在区间里查是否有某个特定的值,随便二分一下,复杂度$T log inf+\frac

主席树 或者 离散化+分块 BZOJ 4636

4636: 蒟蒻的数列 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 381  Solved: 177[Submit][Status][Discuss] Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一

莫队+分块 BZOJ 3809

3809: Gty的二逼妹子序列 Time Limit: 80 Sec  Memory Limit: 28 MBSubmit: 1634  Solved: 482[Submit][Status][Discuss] Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)

(树分块) bzoj 1086

1086: [SCOI2005]王室联邦 Time Limit: 10 Sec  Memory Limit: 162 MBSec  Special JudgeSubmit: 730  Solved: 403[Submit][Status][Discuss] Description “余”人国的国王想重新编制他的国家.他想把他的国家划分成若干个省,每个省都由他们王室联邦的一个成员来管理.他的国家有n个城市,编号为1..n.一些城市之间有道路相连,任意两个不同的城市之间有且仅有一条直接或间接的道路.

bzoj 1101 [POI2007]Zap - 莫比乌斯反演

Description FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a ,y<=b,并且gcd(x,y)=d.作为FGD的同学,FGD希望得到你的帮助. Input 第一行包含一个正整数n,表示一共有n组询问.(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个 正整数,分别为a,b,d.(1<=d<=a,b<=50000) Output 对于每组询问,输出到输出文件zap.out一个正

●BZOJ 4176 Lucas的数论

题链: http://www.lydsy.com/JudgeOnline/problem.php?id=4176 题解: 莫比乌斯反演,杜教筛 首先有这么一个结论: 令d(n)表示n的约数的个数(就是题目中的f(n)),则有 $$d(nm)=\sum_{i|n}\sum_{j|m}[gcd(i,j)==1]$$ ●BZOJ 3994 [SDOI2015]约数个数和也用到了这个东西. 那么就下来接直接进行求ANS的式子的推导: $$\begin{aligned}ANS&=\sum_{n=1}^{N

HDU 5726 GCD

传送门 GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Problem DescriptionGive you a sequence of $N(N≤100,000)$ integers : $a_1,\cdots,a_n(0<a_i≤1000,000,000)$. There are $Q(Q≤100,000)$ queries. For each query $l

GCD (hdu 5726)

GCD Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 1497    Accepted Submission(s): 483 Problem Description Give you a sequence of N(N≤100,000) integers : a1,...,an(0<ai≤1000,000,000). There ar