题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4407
题目大意:
刚开始有一个长度为N的序列,排列为{1、2、3、…、N}。下边是M个操作指令。
有两种操作:
操作1:给你三个整数 Left Right d 求出区间[Left,Right]上与整数d互素的数的和
操作2:给你两个整数 pos d 将第pos个位置上的数改为d。
解题思路:
求出区间[Left,Right]上与整数d互素的数的和可以用容斥定理求出,类似于HDU4135。
下边考虑更改操作。看了题意,1 <= M <= 1000。最多只有1000个操作,那么可以把
每次的更改操作都保存起来,在求[Left,Right]上与整数d互素的数的和时,可以先求出
原先未改变时的结果,再加上改变操作中改变的结果,即为最终结果。具体参考代码。
参考博文:http://blog.csdn.net/ok_again/article/details/11167313
AC代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define LL __int64 using namespace std; int GCD(int a,int b) { if(b == 0) return a; return GCD(b,a%b); } int Factor[22],ct; int A[1100],B[1100]; //A[]纪录更改位置(下标) B[]纪录更改的值 void Divide(int N) { ct = 0; for(int i = 2; i <= sqrt(N*1.0); ++i) { if(N % i == 0) { Factor[ct++] = i; while(N % i == 0) N /= i; } } if(N != 1) Factor[ct++] = N; } LL Solve(int Left, int Right) { LL ans = 0; for(int i = 1; i < (1 << ct); ++i) { int odd = 0; int tmp = 1; for(int j = 0; j < ct; ++j) { if((1 << j) & i) { odd++; tmp *= Factor[j]; } } LL Num = Right / tmp - (Left-1) / tmp; //区间[L,R]中因子tmp的倍数个数 LL Cnt = Num * (Num+1) / 2 * tmp + ((Left-1)/tmp) * tmp * Num; //[L,R]上因子tmp的倍数和 if(odd & 1) //奇加偶减 ans += Cnt; else ans -= Cnt; } return (LL)(Left+Right) * (LL)(Right-Left+1) / 2 - ans; //得到区间[L,R]与d互质的数的和 } int main() { int T,N,M,op; int Left,Right,d,pos; scanf("%d",&T); while(T--) { scanf("%d%d",&N,&M); int id = 0; while(M--) { scanf("%d",&op); if(op == 1) { scanf("%d%d%d",&Left,&Right,&d); Divide(d); LL ans = Solve(Left,Right); for(int i = 0; i < id; ++i) { if(A[i] >= Left && A[i] <= Right) { if(GCD(d,A[i]) == 1) //因为原先第i位置上的数为i。计算[L,R]中互质数已经计算过一次,所以要减去 ans -= A[i]; if(GCD(d,B[i]) == 1) //如果更改的数与d互质,则加上该数。 ans += B[i]; } } printf("%I64d\n",ans); } else { int Flag = 0; scanf("%d%d",&pos,&d); for(int i = 0; i < id; ++ i) { if(A[i] == pos) { Flag = 1; B[i] = d; break; } } if(!Flag) { B[id] = d; A[id++] = pos; } } } } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-12 23:40:03