HDU 4048 Zhuge Liang's Stone Sentinel Maze

题意:给出m种不同重量的石子, 每一种石子的个数无穷多。从中选出N个石子,排成一个环,使得环上的石子重量的最大公约数为1. 旋转同构视为相同方案。

解法:问题转化为从m种数中找出n(循环节个数)个最大公约数为1的数字的排列数,于是想到找补集:满足最大公约数为2的充分必要条件是所选的数都能被2整除,满足最大公约数为3的充分必要条件是所选的数都能被3整除,满足最大公约数为6的方案被重复计算了,于是要容斥原理解决。

       即对于m个数,msqrt(m)时间内统计这m个数中能被i整除的个数nn[i]然后对于n', 假设g[i] = cnt[i] ^n,那么g[i]就是长度为N'的,最大公约数为i倍数的方案数。容斥时注意剪枝。

       题目中n可能于模数10007相同,此时不存在乘法逆元,因此不能通过乘法逆元除n,要将模数乘以n,然后在模意义下做二进制模乘以防溢出(貌似与Rho-Pollard有关,没仔细研究)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;
public class Main{
    int p[] = new int[2300], nn[] = new int[20010];
    void build() {
        boolean vis[] = new boolean[20010];
        int cnt = 0;
        p[0] = 1;
        for (int i = 2; i < 20010; i++)
            if (!vis[i]) {
                p[++cnt] = i;
                int j = i;
                while (j < 20010) {
                    vis[j] = true;
                    j += i;
                }
            }
    }

    StreamTokenizer in = new StreamTokenizer(new BufferedReader(
            new InputStreamReader(System.in)));

    int nextInt() throws IOException {
        in.nextToken();
        return (int) in.nval;
    }

    int prime[] = new int[10010], count[] = new int[10010], cnt;
    void divide(int n) {
        cnt = 0;// 质因子数目
        int mx = (int) (Math.sqrt(n) + 3);
        if (n < mx)
            mx = n;
        for (int i = 2; i < mx; i++) {
            if (n == 1)
                break;
            if (n % i == 0) {
                prime[++cnt] = i;
                count[cnt] = 0;
                while (n % i == 0) {
                    n /= i;
                    count[cnt]++;
                }
            }
        }
        if (n != 1) {
            prime[++cnt] = n;
            count[cnt] = 1;
        }
    }

    long pow(long x, long p) {
        long ans = 1;
        while (p > 0) {
            if ((p & 1) == 1)
                ans = muti_mod(ans,x);
            p >>= 1;
            x = muti_mod(x,x);// x*x can be long even x is int
        }
        return ans;
    }

    long muti_mod(long a, long b)
    {
        long n=mod;
        long exp = a % n, res = 0;
        while (b>0)
        {
            if ((b & 1)==1)
            {
                res += exp;
                if (res > n)
                    res -= n;
            }
            exp <<= 1;
            if (exp > n)
                exp -= n;

            b >>= 1;
        }
        return res;
    }

    long get(int now, int sum, long k) {
        long ans = pow(nn[sum], k);
        for (int i = now + 1; p[i]*sum<=mx; i++)
            if (nn[p[i]*sum] >0)
                ans = (ans - get(i, sum * p[i], k) + mod)% mod;
        return ans;
    }

    long ans, mx;
    void dfs(int now, long value, long euler) {
        if (now == cnt + 1) {// value是n的因子
            long temp = get(0, 1, n / value);
            ans = (ans + euler * temp) % mod;
            return;
        }
        dfs(now + 1, value, euler);
        long temp = prime[now];
        for (int i = 1; i <= count[now]; i++) {
            dfs(now + 1, value * temp, muti_mod(euler * (prime[now] - 1),
                    (temp / prime[now]) ));
            temp = muti_mod(prime[now],temp);
        }
    }

    long polya() {
        ans = 0;
        divide(n);
        dfs(1, 1, 1);
        ans/=n;
        mod/=n;
        ans%=mod;
        ans+=mod;
        ans%=mod;
        return ans;
    }

    int m, n;
     int mod ;
    void run() throws IOException {
        int cas = nextInt();
        build();
        while (cas-- > 0) {
            m = nextInt();
            n = nextInt();
            mod= 10007*n;
            Arrays.fill(nn, 0);
            mx = -1;
            for (int i = 0; i < m; i++) {
                int x = nextInt();
                if (x > mx)
                    mx = x;
                int j;
                for (j = 1; j * j < x; j++)
                    if (x % j == 0) {
                        nn[j]++;
                        nn[x / j]++;
                    }
                if (j * j == x)
                    nn[j]++;
            }
            System.out.println(polya());
        }
    }

    public static void main(String[] args) throws IOException {
        new Main().run();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值