#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <iostream>
#include <map>
using namespace std;
typedef long long ll;
#define INF 2147483648
//#define INF 2147438647
const int N = 407, M = 10007, mod = 998244353;
using namespace std;
int gcd(int a, int b)
{
if(b == 0)return a;
return gcd(b, a % b);
}
int n, m, q, u, block_num;
int v[N][N];
int block, bi[N];
int g[N][N];//g[i][j] 表示的是第i块第j个点的前缀gcd,一定是单调递减的,且不同的个数最多为log(ai)个
int G[N];//G[i]表示的是第i块的整块的前缀gcd
int sum[N];//sum[i]表示的是第i块的整块的前缀异或和
map <int, int> f[N];//f[x][tmp] = y;表示的是从第一个点到第x块的前缀异或和为tmp的位置为y
int kcase;
void modify(int u)
{
int x = (u - 1) / block + 1;
int y = (u - 1) % block + 1;
scanf("%d", &v[x][y]);
f[x].clear();
int tmp = 0;
g[x][0] = x ? G[x - 1] : 0;//判断边界
for(int j = 1; j <= block; ++ j){//整个快的前缀gcd以及前缀异或和都要改变
tmp ^= v[x][j];
if(f[x].find(tmp) == f[x].end())
f[x][tmp] = j;
g[x][j] = gcd(v[x][j], g[x][j - 1]);
G[x] = g[x][j];
}
sum[x] = tmp;
}
int query(ll k)
{
int tmp = 0;
int gtmp = v[1][1];
//直接暴力跑,如果前缀gcd和上一块相同,直接O(1)查找答案。
//如果不相同,暴力找答案。因为不相同的前缀gcd只有log(ai)个,所以可以暴力
for(int i = 1; i <= block_num; ++ i){
if(!i || G[i] != G[i - 1]){
for(int j = 1; j <= block; ++ j){
tmp ^= v[i][j];
gtmp = g[i][j];
if((ll)tmp * gtmp == k){
return (i - 1) * block + j - 1;
}
}
}
else {
gtmp = G[i];
//需要判断一下当前值是否小于int
if(k % gtmp == 0 && k / gtmp < INF && f[i].find(k / gtmp ^ tmp) != f[i].end()){
return (i - 1) * block + f[i][k / gtmp ^ tmp] - 1;
}
tmp ^= sum[i];
}
}
return -1;
}
int main()
{
scanf("%d", &n);
block = sqrt(n);
int tmp = 0;
for(int i = 1; i <= n; ++ i){
int x = (i - 1) / block + 1;
int y = (i - 1) % block + 1;
scanf("%d", &v[x][y]);
tmp ^= v[x][y];
g[x][y] = gcd(v[x][y], g[x][y - 1]);
G[x] = g[x][y];//一直更新,直到整个块的最后一个
if(f[x].find(tmp) == f[x].end()){//没有就插入
f[x][tmp] = y;
}
if(x != i / block + 1){//该块的最后一个点
sum[x] = tmp;//记录整块的前缀异或和
g[x + 1][0] = G[x];//细节,必须所有的块都连贯起来
tmp = 0;//清零
}
}
scanf("%d", &q);
char op[100];
block_num = ceil((double)n / block);
while(q -- ){
scanf("%s", op);
if(op[0] == 'M'){
scanf("%d", &u);
modify(u + 1);//输入的点是从0开始的,我们的点是从1开始的
}
else {
ll k;
scanf("%lld", &k);
int res = query(k);
if(res == -1)
puts("no");
else printf("%d\n", res);
}
}
return 0;
}
luogu P4108 [HEOI2015]公约数数列(分块、gcd性质)
最后发布:2020-11-22 20:14:12首次发布:2020-11-22 20:14:12