gcd的性质+分块 Bzoj 4028

摘要:
定义pregcd表示目前这个块之前所有的数字的gcd,prexor为目前这个块之前所有的数字的xor。然后,如果这个块中,他的gcd[r[i]]和pregcd求gcd以后没有发生变化,那么就表示可能存在xor[j]^prexor*=val。//取物问题一定要小心先手胜利的条件#includeusingnamespacestd;#pragmacomment#defineLLlonglong#defineALLa.begin(),a.end()#definepbpush_back#definemkmake_pair#definefifirst#definesesecond#definehahaprintf/*首先,我们对所有的东西进行分块,然后我们对每个区间进行处理,分别得到这个区间的gcd和xor。

4028: [HEOI2015]公约数数列

Time Limit:10 SecMemory Limit:256 MB
Submit:865Solved: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 namespacestd;
#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
")
/*首先,我们对所有的东西进行分块,然后我们对每个区间进行处理,分别得到这个区间的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];
intl[maxn], r[maxn], block, num, belong[maxn];
intn, q;
set<LL>S[maxn];

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

voidbuild(){
    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(intp){
    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]);
    }
}

voidquery(LL val){
    LL prexor, pregcd;
    for (int i = 1; i <= r[1]; i++){
        if (gcd[i] * Xor[i] ==val){
            printf("%d
", 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
", 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
", j - 1); return;
                }
            }
        }
        pregcd =get_gcd(pregcd, gcd[r[i]]);
        prexor = prexor ^Xor[r[i]];
    }
    printf("no
");
}

intmain(){
    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'){
            intp; LL val;
            scanf("%d%lld", &p, &val);
            a[++p] =val;
            update(belong[p]);
        }
        else{
            LL val;
            scanf("%lld", &val);
            query(val);
        }
    }
    return 0;
}
View Code

免责声明:文章转载自《gcd的性质+分块 Bzoj 4028》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇ArcGIS JavaScript API4.8 底图选择的几种方案Android系统中的广播(Broadcast)机制简要介绍和学习计划下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

封装GCD以及介绍如何使用

源码地址 http://pan.baidu.com/s/1zTUR8 研究GCD有一段时间,翻译了多篇文章,找了很多的资料,看了很多官方文档,看起来很难,实际上很简单,本人一一进行讲解怎么使用. 支持ARC以及非ARC,无论在ARC环境还是在非ARC环境,都需要调用dispatchRelease方法来释放init出的GCDGroup,GCDQueue,G...

QAC报告中的STCYC

指圈复杂度,目前主要关注参数。精髓:覆盖所有的可能情况最少使用的测试用例个数。 一般来说,圈复杂度大于10的方法存在很大的出错风险。  E表示控制流图中边的数量,N表示控制流图中节点的数量。 圈复杂度的计算公式为:V(G) = E - N + 2 圈复杂度的计算还有另外一种更直观的方法,因为圈复杂度所反映的是“判定条件”的数量,所以圈复杂度实际上就是等于...

bzoj网络流

近期看了一些bzoj的网络流,深感智商不够。不过对于网络流又有了进一步的理解。 还是mark一下吧。 献上几篇论文:1)《最小割模型在信息学竞赛中的应用》                     2)《浅析一类最小割问题》 1、bzoj1066(最大流) 题意:戳这里 思路:很明显拆点最大流模型,然后对于每个点每个高度流量限为1,那么根据最大流即为可以出去...

BZOJ 1915 [Usaco2010 Open]奶牛的跳格子游戏

BZOJ_1915     如果我们把一对相邻的来去经过的格子看成一个研究对象的话,那么相邻研究对象之间距离不超过K,而且相邻研究对象之间所有正数的格子都可以在向前跳的过程中经过。抽象成这样的模型之后就可以用单调队列+dp搞定最后一个研究对象在位置i时的最优解。这时由于在向前跳的过程中还可能经过最右边研究对象的右边的一些距离不超过K-1的正数的格子,最后扫...

CentOS设置密码复杂度及过期时间等

我们在使用linux系统设置密码的时候,经常遇到这样的问题,系统提示:您的密码太简单,或者您的密码是字典的一部分。那么系统是如何实现对用户的密码的复杂度的检查的呢?  系统对密码的控制是有两部分(我知道的)组成:  1 cracklib  2 login.defs  声明:login.defs主要是控制密码的有效期。对密码进行时间管理。此处不细谈。  lo...

hash函数的选择

哈稀函数按照定义可以实现一个伪随机数生成器(PRNG),从这个角度可以得到一个公认的结论:哈希函数之间性能的比较可以通过比较其在伪随机生成方面的比较来衡量。 一般来说,对任意一类的数据存在一个理论上完美的哈希函数。这个完美的哈希函数定义是没有发生任何碰撞,这意味着没有出现重复的散列值。在现实中它很难找到一个完美的哈希散列函数,而且这种完美函数的趋近变种在实...