Bzoj3853 GCD Array
Time Limit: 6 Sec Memory Limit: 64 MB
Submit: 328 Solved: 125
Description
Input
Output
Sample Input
1 4 1 2
2 5
1 3 3 3
2 3
0 0
Sample Output
6
7
HINT
Source
数学问题 莫比乌斯反演
我们注意到每次修改的位置是不连续的,很难用数据结构统一维护。但是这个数据范围看上去必须用数据结构维护,那么我们就得想办法让每次修改/查询的部分连续起来。
方法一:分块。
将修改的数量分块,如果$ gcd(x,n)=d $的个数大于 $ sqrt(i) $个,暴力修改,如果小于$sqrt(i)$,分块修改,有一部分带修改点构成了有规律的区间。
常数优秀的话应该可以过(口胡,没尝试过)
方法二:莫比乌斯反演
我们把题目中各个关键条件都写在草稿纸上,这样有很大几率会看到类似$ v [gcd(x,n)=d]$ 的式子
看到这个形式,我的内心充满波动,甚至想要反演!
试着推一下式子:
$$ v[gcd(x,n)=d] $$
$$ =v[gcd(x/d,n/d)=1] $$
$$ =v * \sum_{k|\frac{x}{d} , k|\frac{n}{d}} \mu(k)$$
把
$[k|\frac{x}{d}] \mu(k)$
变形成
$[kd|x] \mu(k)$
那么我们就可以O($\sqrt n$)地枚举 $\frac{n}{d}$的因数k,再单点修改每一个 $kd|x$ 的位置。
(x是不确定的位置,修改了每一个kd,就等于修改了所有受影响的x)
嗯?我们好像并没有解决单点修改的问题?
但我们可以区间查询了!
设原来要维护的数组是a[],现在维护的数组是c[],
那么有
$$a[i]=\sum_{j|i}c[j]$$
于是
$$ans=\sum_{i=i}^{x} c[i] =\sum_{i=i}^{x} \sum_{j|i}c[j] $$
$$ans=\sum_{j=1}^{x}c[j]* \lfloor \frac{x}{j} \rfloor$$
可以应用分块加速的套路。
用数据结构维护c[j]的前缀和即可。
一个脑洞:
如果每次给定k和b,修改满足"kx+b<=n"的"kx+b"位置,要怎么玩?(k,b,x都是整数)
“有问必答不知道”
另一个脑洞:
如果每次修改$ lcm(x,n)=d $ 的位置,要怎么玩?(x,n,d都是整数)
我们试着推一下:
$$ v[lcm(x,n)=d] $$
$$ v[gcd(x,n)=\frac{x*n}{d} ]$$
$$ v[gcd(d/n,d/x)=1]$$
$$ =v * \sum_{k|\frac{d}{x} , k|\frac{d}{n}} \mu(k)$$
我们发现这次x到了分母的位置,且d每次修改的时候都会变,维护起来就很麻烦……
嗯……我编不下去了!(逃)
原题代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<cstring> 4 #include<cstdio> 5 #include<cmath> 6 #include<vector> 7 #define LL long long 8 using namespace std; 9 const int mxn=200010; 10 int read(){ 11 int x=0,f=1;char ch=getchar(); 12 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 14 return x*f; 15 } 16 int n,Q; 17 LL t[mxn]; 18 void add(int x,int v){ 19 while(x<=n){t[x]+=v;x+=x&-x;}return; 20 } 21 LL smm(int x){ 22 LL res=0; 23 while(x){res+=t[x];x-=x&-x;} 24 return res; 25 } 26 // 27 int pri[mxn],mu[mxn],cnt=0; 28 bool vis[mxn]; 29 vector<int>ve[mxn]; 30 void init(){ 31 mu[1]=1; 32 for(int i=2;i<mxn;i++){ 33 if(!vis[i]){ 34 pri[++cnt]=i; 35 mu[i]=-1; 36 } 37 for(int j=1;j<=cnt && pri[j]*i<mxn;j++){ 38 vis[pri[j]*i]=1; 39 if(i%pri[j]==0){mu[i*pri[j]]=0;break;} 40 mu[pri[j]*i]=-mu[i]; 41 } 42 } 43 for(int i=1;i<mxn;i++) 44 for(int j=i;j<mxn;j+=i) 45 ve[j].push_back(i); 46 return; 47 } 48 LL calc(int x){ 49 LL res=0; 50 for(int i=1,pos;i<=x;i=pos+1){ 51 int y=x/i; pos=x/y; 52 res=res+(smm(pos)-smm(i-1))*(LL)y; 53 } 54 return res; 55 } 56 int main(){ 57 int i,j; 58 init(); 59 int cas=0; 60 while(scanf("%d%d",&n,&Q)!=EOF){ 61 if(!n && !Q)break; 62 printf("Case #%d:\n",++cas); 63 memset(t,0,sizeof t); 64 int op,x,v,d; 65 while(Q--){ 66 op=read(); 67 if(op==1){ 68 x=read();d=read();v=read(); 69 if(x%d==0){ 70 int st=x/d; 71 for(j=0;j<ve[st].size();j++){ 72 int dx=ve[st][j]; 73 add(dx*d,v*mu[dx]); 74 } 75 } 76 } 77 else{ 78 x=read(); 79 LL ans=calc(x); 80 printf("%lld\n",ans); 81 } 82 } 83 } 84 return 0; 85 }