HDU 5514 容斥原理

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define LL long long
const 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;
}

你可能感兴趣的:(----数学题----)