hdu Sum 容斥定理的应用
http://acm.hdu.edu.cn/showproblem.php?pid=4407
比赛的时候想到了先求与p不互质的然后减去,但是就是遇到了容斥处理的问题,当时没有想到对因子容斥,只想到求和容斥那去了。。哎。悲剧啊、、
题意:
给出一个长度为n序列初始值为1,2,......n对其进行两种操作:
"2 x c" 将x位置的值替换成y;
"1 x y p" 求区间[x,y]内与p互质的数的和。
思路:
首先对于2操作我们离线处理,因为只有对于1,2,3.....n这样的序列我们在求1操作时才好处理,首先对p进行因式分解,可知他最多有6个因子因为到17是就已经大于400000了,然后我们知道对于区间[1,n] 内能被p1整除的数目为n/p1 也即 p1,2*p1,3*p1....n/p1*p1。这是一个等差数列我们直接求个即可。假设p分解出来的质因子为p1,p2,p3.那么我们分别对其用等差数列求和然后容斥掉有公共因子的即可。
View Code
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Max(a , b) ((a) > (b) ? (a) : (b)) #define ll __int64 #define inf 0x7f7f7f7f #define MOD 100000007 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 100007 #define N 400007 using namespace std; //freopen("din.txt","r",stdin); int p[660],prim[660],idx; int tmp[30]; map<int,int>mp; int n,m; //sqrt(400000)=632....枚举出660以内的质数即可 void getPrime(){ int i,j; CL(p,0); for (i = 2; i*i < 660; ++i){ if (!p[i]){ for (j = i + i; j < 660; j += i) p[j] = 1; } } idx = 0; for (i = 2; i < 660; ++i){ if (!p[i]) prim[idx++] = i; } } ll getS(int x){ return ((1ll + (ll)x)*(ll)x)/2; } int gcd(int a,int b){ if (b == 0) return a; return gcd(b,a%b); } ll cal(int p,int x){ int i,j; int len = 0; //分解质因子 for (i = 0; i < idx; ++i){ if (p % prim[i] == 0){ tmp[len++] = prim[i]; while (p%prim[i] == 0){ p /= prim[i]; } } if (p == 1) break; } if (p != 1) tmp[len++] = p; //状态压缩+容斥 ll ans = 0; for (i = 1; i < (1<<len); ++i){ int ct = 0; int fac = 1; for (j = 0; j < len; ++j){ if (i&(1<<j)){ fac *= tmp[j]; ct++; } } int k = x/fac; if (ct&1){ ans += (((ll)fac + (ll)k*fac)*(ll)k)/2; } else{ ans -= (((ll)fac + (ll)k*fac)*(ll)k)/2; } } return ans; } int main(){ //freopen("din.txt","r",stdin); getPrime(); int t; map<int,int>::iterator it; int op,x,y,p; scanf("%d",&t); while (t--){ mp.clear(); scanf("%d%d",&n,&m); while (m--){ scanf("%d",&op); if (op == 2){ scanf("%d%d",&x,&y); mp[x] = y;//map来确定最后x为之修改成了y } else{ scanf("%d%d%d",&x,&y,&p); ll res = 0; res = getS(y) - getS(x - 1) - (cal(p,y) - cal(p,x - 1));//先求[x,y]区间的值,然后减去[x,y]与p不互质的数 //更新修改后的,离线处理 for (it = mp.begin(); it != mp.end(); ++it){ if (it->first != it->second && it->first >= x && it->first <= y){ if (gcd(it->first,p) == 1) res -= it->first; if (gcd(it->second,p) == 1) res += it->second; } } printf("%I64d\n",res); } } } return 0; }