hdu 4611
题意:求累加abs(i%a - i%b)的和,(o<=i<n);
分析:首先a,b大小无关,不妨设a<b,通过枚举几项,发现按照每a个来分,abs(i%a-i%b)在大部分区间里的值是相同的,只有在k*b属于这个区间里的时候发生变化(因为余数都是递增的只有在k*b出现后从b-1变成0);
所以我们不妨枚举按长度为a来枚举起点,如果k*b属于这个区间就另外处理,当到达循环节后直接处理到最终余数部分继续枚举,最后如果大于n就break,并且减去多加的部分;
对于样例:21 2 5
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ..... 21
0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0
每2个一组,每组的对应位置差值是相同,可以o(1)处理每一组,
第三组因为4在里面,所以分成两段,也可以o(1)处理,
处理到最小公倍数10后可以一次处理到20,然后继续处理,最坏的情况下a,b很大且互质,时间复杂度O(max(a,b));
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #include<cmath> 5 #include<algorithm> 6 #include<cstdlib> 7 using namespace std; 8 const int N = 10000; 9 typedef long long LL; 10 int gcd(int a,int b){ 11 if (b==0) return a; 12 else return gcd(b,a%b); 13 } 14 int a,b,n; 15 int main(){ 16 int T;scanf("%d",&T); 17 while (T--){ 18 scanf("%d%d%d",&n,&a,&b); 19 if (a>b) swap(a,b); 20 int c = a/gcd(a,b)*b; 21 int now = 0,now2 = b; 22 LL ans = 0; 23 while (now < n){ 24 if (now <= now2 && now2 < now+a){ 25 ans +=(LL)abs(now%a-now%b)*(now2-now) + (LL)abs(now2%a-now2%b)*(now+a-now2); 26 now2 += b; 27 }else { 28 ans += (LL)abs(now%a-now%b)*a; 29 } 30 now += a; 31 if (now == c){ 32 ans = ans*(n/c); 33 now = n/c*c; 34 now2 = now + b; 35 } 36 } 37 if (now>=n){ 38 for (int i=now-1;i>=n;i--){ 39 ans -= abs(i%a-i%b); 40 } 41 printf("%I64d\n",ans); 42 continue; 43 } 44 printf("%I64d\n",ans); 45 } 46 return 0; 47 }