• hdu5514-Frogs(容斥原理)好题


    题意:有m个石头围成一圈,编号分别为0到m-1,现在有n只青蛙,都在0号石头上,第i只青蛙会从当前编号为p的石头跳到编号为(p+ai)%m的石头上。被青蛙经过的石头都会被占领,求这m块石头中所有被占领过的石头的编号和。

    题解:对于第i只青蛙,它所能跳到的最小的位置是gcd(ai, m)

                   设最小位置为z,需要跳x圈,跳了y步,可得方程:x*m+z=ai*y

                   即:x*m-ai*y = z  由扩展欧几里得定理可知,z的最小整数解为gcd(m,ai)

           因为对于单独的每一只青蛙计算结果会重复计算,所以利用容斥对每一个m的因子计算。

           首先对于每一个x=gcd(ai,m),如果m的一个因数fac%x==0,那么fac就会被跳到。

           然后对于每一个会碰到的因数计算,当m的一个因数j的因数i被计算的时候,j就会被重复计算,要减去。

           虽然题解很有道理,但是我想了好久也没明白容斥不是奇加偶减吗,怎么这么算了= =

           后来搜题解明白了此题gcd太多,二进制枚举会爆longlong,dfs也会超时,http://www.acmtime.com/?p=864 一个神奇的剪枝。

    #include <algorithm>
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    
    using namespace std;
    
    const int N = 100005;
    int fac[N], cnt;
    int cc[N];
    
    void cal(int x) {
        cnt = 0;
        int limit = sqrt(x);
        fac[cnt++] = 1;
        for (int i = 2; i < limit; ++i) {
            if (x % i == 0) fac[cnt++] = i, fac[cnt++] = x/i;
        }
        if (limit*limit == x) fac[cnt++] = limit;
        else if (x % limit == 0) fac[cnt++] = limit, fac[cnt++] = x/limit;
        sort(fac, fac+cnt);
    }
    
    int main()
    {
        int T, cas = 0;
        int n, m;
        scanf("%d", &T);
        while (T--) {
            printf("Case #%d: ", ++cas);
            scanf("%d%d", &n, &m); //1e4 1e9
            cal(m);
            memset(cc, 0, sizeof cc);
            int ai;
            for (int i = 0; i < n; ++i) {
                scanf("%d", &ai);
                int gcd = __gcd(ai, m);
                for (int i = 0; i < cnt; ++i) {
                    if (fac[i] % gcd == 0) cc[i] = 1;
                }
            }
            long long ans = 0;
            for (int i = 0; i < cnt; ++i) {
                if (cc[i] == 0) continue;
                long long tmp = (m-1) / fac[i];
                ans += tmp * (tmp+1) / 2 * fac[i] * cc[i];
                for (int j = i+1; j < cnt; ++j) {
                    if (fac[j] % fac[i] == 0) cc[j] -= cc[i];
                }
            }
            printf("%lld
    ", ans);
        }
        return 0;
    }
  • 相关阅读:
    期中架构实现步骤
    安装centos以及优化步骤
    inotify+rsync实现实时热备
    [转]ubuntu安装vncserver实现图形化访问
    [转]电烙铁的使用小技巧
    彻底解决 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
    解读系统托盘图标隐藏(删除)
    一个小公式帮你轻松将IP地址从10进制转到2进制
    [查阅]Dalvik opcodes
    [查阅]MSIL Instruction Set
  • 原文地址:https://www.cnblogs.com/wenruo/p/5964323.html
Copyright © 2020-2023  润新知