Sum HDU - 4407
考察:容斥原理
这题完全不会....本蒟蒻连求出x~y区间p倍数的和都没想出来....虽然在质数距离那道题有涉及但是不完全想写那个公式.....
这道题也是HDU 1695 的变式,那道题是求[x,y]区间与x互质的个数,本质也是转化为1~y互质数-1~x的互质数.
参考大佬的思路:
将[x,y]区间与p互质的和转换成[1,y]区间和-[1,x-1]区间和.这样好求很多.那么我们和就是求出[1,x]区间内不互质的数个数.用(1+n)*n/2再乘上相应的倍数即可.
单点修改可以用map存储,当我们计算完和后就遍历一遍map,看是否需要修改.
注:不要遍历x~y,因为询问x可能很小而y很大.但是总操作数只有1000,也就是说map的size最多1000
时间复杂度: 1000*128*log1000 大约1e6
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <map> 5 using namespace std; 6 typedef long long ll; 7 const int N = 400010; 8 vector<int> v[N]; 9 bool st[N]; 10 map<int,int> mp; 11 void GetPrime(int n) 12 { 13 for(int i=2;i<=n;i++) 14 { 15 if(st[i]) continue; 16 v[i].push_back(i); 17 for(int j=2*i;j<=n;j+=i) 18 { 19 v[j].push_back(i); 20 st[j] = 1; 21 } 22 } 23 } 24 int gcd(int a,int b) 25 { 26 return b?gcd(b,a%b):a; 27 } 28 ll GetSum(int p,int s) 29 { 30 int sz = v[p].size(); ll ans = (ll)(1+s)*s/2; 31 for(int i=1;i<1<<sz;i++) 32 { 33 ll res = 1;int cnt = 0; 34 for(int j=0;j<sz;j++) 35 { 36 if(i>>j&1) 37 { 38 if(res*v[p][j]>s) {res=-1;break; } 39 res*=(ll)v[p][j],cnt++; 40 } 41 } 42 if(res!=-1) 43 { 44 int k = s/res; 45 if(cnt&1) ans -= (ll)(1+k)*k/2*res; 46 else ans+=(ll)(1+k)*k/2*res; 47 } 48 } 49 return ans; 50 } 51 int main() 52 { 53 GetPrime(N-10); 54 int T; 55 scanf("%d",&T); 56 while(T--) 57 { 58 int n,m; mp.clear(); 59 scanf("%d%d",&n,&m); 60 for(int i=1;i<=m;i++) 61 { 62 int op,x,y; scanf("%d%d%d",&op,&x,&y); 63 if(op==1) 64 { 65 int p; scanf("%d",&p); 66 ll ans = GetSum(p,y)-GetSum(p,x-1);//1~n比x~y更好求 67 for(auto& it:mp) 68 { 69 if(it.first>=x&&it.first<=y) 70 { 71 int i = it.first; 72 int t = gcd(p,mp[i]),k = gcd(p,i); 73 if(t==1) ans+=mp[i]; 74 if(k==1) ans-=i; 75 } 76 } 77 printf("%lld\n",ans); 78 }else{ 79 mp[x] = y; 80 } 81 } 82 } 83 return 0; 84 }
还是得写点总结啊,不然写了白写..
总结:
- [x,y]区间求p倍数的和转化为[1,x]与[1,y]区间和的差
- p倍数的和可以将p倍数单独拎出来用(1+n)*n/2求和
时隔五个月复习我还是不会,主要卡在这几点:
- 与p互质的和还是没反应过来是对p质因数分解,学多了基础的反而不扎实了.
- 修改操作没反应过来可以存储修改操作,因为容斥是建立在1~n的基础上的,不进行容斥就只能一个个计算.
- 这里的map遍历是需要在函数外面,里面就莫名多遍历一遍.