HDU 5514 容斥原理

来源:互联网 发布:java属性是什么意思 编辑:程序博客网 时间:2024/06/05 20:14

HDU 5514
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5514
题意:
几只青蛙(1e4)从0点出发,每次可以跳ai个石头到另外一个石头上。石头有m个且是循环摆放,青蛙也只能朝一个方向跳石头。
问最后能调到的石头编号和。
思路:
大容斥。
复现并没有做出。很容易想到一个数在m中只能走到他们公约数倍数的点。想用质因数分解做,结果发现这些公约数可能是几个质因数的积。也就是不能单独算一个质因子来做容斥原理。如果对每一个数都求出公约数,得出1e4个公约数,然后用类似状压来表示容斥原理那个公式……2^1e4。
首先感谢阳神~
正解是对m分解出它所有的约数。然后记录两个数组f,g。f表示这个约数与数组中一个数a的gcd(a,m)整除则为1,g用于记录使用了几次。
一开始g全部记为0。从前往后找,发现f!=g的约数i则进行运算使得f=g,并且对其他的是i的倍数的约数的g值进行相应修改。因为约数个数有限(最多log2(m))这样,所以这样log2(m)^2 + O(n)的复杂度是可以过的。
细节处理就是m这个点是不能访问的,所以预处理数据之后置f[mark_of_m] = 0。
源码:

#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <algorithm>#include <iostream>#include <string>#include <vector>#include <queue>using namespace std;#define LL long longconst int MAXN = 1e5;int p[MAXN], cnt;LL vis[MAXN], num[MAXN];int gcd(int a, int b){    if(b == 0)  return a;    return gcd(b, a % b);}int main(){    int T;    scanf("%d", &T);    for(int cas = 1 ; cas <= T ; cas++){        int n, m;        scanf("%d%d", &n, &m);        cnt = 0;        for(int i = 1 ; i <= sqrt(m) ; i++){            if(m % i == 0){                p[cnt++] = i;                if(i * i != m)  p[cnt++] = m / i;            }        }//        p[cnt++] = m;        sort(p, p + cnt);        memset(vis, 0, sizeof(vis));        memset(num, 0, sizeof(num));        int u;        for(int i = 0 ; i < n ; i++){            scanf("%d", &u);            int temp = gcd(u, m);            for(int j = 0 ; j < cnt ; j++)                if(p[j] % temp == 0)   vis[j] = 1;        }        LL ans = 0;        vis[cnt - 1] = 0;//        printf("cnt = %d\n", cnt);//        m--;        for(int i = 0 ; i < cnt ; i++){            if(vis[i] != num[i]){//                printf("vis[%d] = %d, num[%d] = %d\n", i, vis[i], i, num[i]);                LL temp = m / p[i];                ans += temp * (temp + 1) / 2 * p[i] * (vis[i] - num[i]);                temp = vis[i] - num[i];                for(int j = i ; j < cnt ; j++){                    if(p[j] % p[i] == 0){//                        ans += (LL)(vis[i] - num[i]) * p[j];                        num[j] += temp;//                        printf("j = %d, p[j] = %d, num[j] = %d\n", j, p[j], num[j]);                    }                }//                printf("i = %d, p[%d] = %d, ans = %I64d\n", i, i, p[i], ans);            }        }        printf("Case #%d: %I64d\n", cas, ans);    }    return 0;}
0 10
原创粉丝点击