看样子分块题应该做的还不够。
题目描述
设计一个数据结构. 给定一个正整数数列 (a_0, a_1, ldots , a_{n-1}),你需要支持以下两种操作:
MODIFY id x
: 将 (a_{mathrm{id}}) 修改为 (x).QUERY x
: 求最小的整数 (p(0 leq p < n)),使得 (gcd(a_0, a_1, ..., a_p) cdot operatorname{XOR}(a_0, a_1, ..., a_p) = x). 其中 (operatorname{XOR}(a_0, a_1, ..., a_p)) 代表 (a_0, a_1, ldots , a_p) 的异或和,(gcd) 表示最大公约数。输入输出格式
输入格式:
输入数据的第一行包含一个正整数 (n)。
接下来一行包含 (n) 个正整数 (a_0, a_1, ..., a_{n-1})。
之后一行包含一个正整数 (q),表示询问的个数。
之后 (q) 行,每行包含一个询问。格式如题目中所述。
输出格式:
对于每个
QUERY
询问,在单独的一行中输出结果。如果不存在这样的 (p),输出no
.输入输出样例
样例输入:
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
输出样例:
6 0 no 2 8 8
数据范围与约定
对于 (100\%) 的数据,(n leq100000, q leq 10000, a_i leq 10^9 (0 leq i < n)),
QUERY x
中的 (x leq 10^{18}),
MODIFY id x
中的 (0 leq mathrm{id} < n, 1 leq x leq 10^9)。
题解:
感觉需要推导的东西还是比较多的。但是以后要记着看到奇怪的(?)数据范围时要想到分块。
这个题有一定的特点,就是两个表达式都是前缀形式。对于 (gcd) 而言,它一定是递减的,而且不同的数值最多只有 (O(log a_i)) 个,因为每次至少会变为原来的 (frac 12)。因此产生变化的位置是可以考虑枚举的。
注意到询问是 (10000) 的,不是很大。我们对原区间进行 (O(sqrt n)) 分块。
因为要找的是最小的,所以应该从前往后枚举。对于 (gcd) 整块不变的那些块,我们只需要找到其中是否存在异或前缀和恰好为 (x/gcd) 的。这一询问可以用 std::map
来解决。
当涉及单点修改时,我们只需要对当前块中的 map
进行修改就可以了。
实际上 map
中存的是当前块中的异或前缀和,由于我们每次查询都是从前往后查,可以积累一定的信息。因此不必也不能维护全局前缀和,不然修改时影响的范围太大。
不过分块的细节挺多的,包括边界一类的。还要注意复杂度,不能把 gcd()
函数当 (O(1)) 的操作用啊啊啊啊啊啊…= =
时间复杂度 (O(nlog a_i+qsqrt nlog a_i))。
Code:
#include<cstdio>
#include<cstring>
#include<map>
#include<cmath>
#define inf 2147438647
using std::map;
int gcd(int x,int y)
{
while(y)
{
int t=y;
y=x%y;
x=t;
}
return x;
}
map<int,int> f[400];
int a[400][400],g[400][400];
int G[400],sum[400];
int main()
{
int n,m,u;
scanf("%d",&n);
int b=sqrt(n),tmp=0;
for(int i=1;i<=n;++i)
{
int x=(i-1)/b,y=(i-1)%b+1;
scanf("%d",&a[x][y]);
tmp^=a[x][y];
g[x][y]=gcd(a[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/b)
{
sum[x]=tmp;
g[x+1][0]=G[x];
tmp=0;
}
}
scanf("%d",&m);
char op[100];
int tot=ceil((double)n/b)-1;
while(m--)
{
scanf("%s",op);
if(op[0]=='M')
{
scanf("%d",&u);
++u;
int x=(u-1)/b,y=(u-1)%b+1;
scanf("%d",&a[x][y]);
f[x].clear();
tmp=0;
g[x][0]=x?G[x-1]:0;
for(int j=1;j<=b;++j)
{
tmp^=a[x][j];
if(f[x].find(tmp)==f[x].end())
f[x][tmp]=j;
g[x][j]=gcd(a[x][j],g[x][j-1]);
G[x]=g[x][j];
}
sum[x]=tmp;
}
else
{
long long k;
scanf("%lld",&k);
int flag=0;
tmp=0;
int gtmp=a[0][1];
for(int i=0;i<=tot;++i)
{
if(!i||G[i]!=G[i-1])//暴力
{
for(int j=1;j<=b;++j)
{
tmp^=a[i][j];
gtmp=g[i][j];
if((long long)tmp*gtmp==k)
{
flag=1;
printf("%d
",i*b+j-1);
break;
}
}
if(flag)
break;
}
else
{
gtmp=G[i];
if(k%gtmp==0&&k/gtmp<inf&&f[i].find(k/gtmp^tmp)!=f[i].end())
{
printf("%d
",i*b+f[i][k/gtmp^tmp]-1);
flag=1;
break;
}
tmp^=sum[i];
}
}
if(flag)
continue;
puts("no");
}
}
return 0;
}