zoukankan      html  css  js  c++  java
  • 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;
    }
  • 相关阅读:
    Java核心技术 卷一 笔记四 库类的直接使用
    Java核心技术 卷一 笔记三 大数值及数组
    Java核心技术 卷一 笔记2 字符串的复制
    Java核心技术 卷一 笔记1
    修改css 样式后, hover事件 不生效
    修改 element ui input 输入框 样式不生效问题
    css3 计算属性
    Vue3 改动系列
    浏览器实现,向下滑动 鼠标滚轮,页面横向移动
    linux ceont0s7 vue 打包压缩图片 一直报错
  • 原文地址:https://www.cnblogs.com/wenruo/p/5964323.html
Copyright © 2011-2022 走看看