zoukankan      html  css  js  c++  java
  • CodeForces 840C

    思路来自FXXL中的某个链接

    /*
    CodeForces 840C - On the Bench [ DP ]  |  Codeforces Round #429 (Div. 1)
    题意:
    	给出一个数组,问有多少种下标排列,使得任意两个相邻元素的乘积不是完全平方数
    分析:
    	将数组分组,使得每组中的任意两个数之积为完全平方数
    	由唯一分解定理可知,每个质因子的幂次的奇偶性相同的两个数之积为完全平方数
    		即按每个质因子的幂次的奇偶性分组,故这样的分组唯一
    	然后问题归结于每组中的数不能相邻的排列有几种
    	设 dp[i][j]表示 前i组相邻的同组的数有j对
    	考虑把第i+1组分段后插入前i组的空隙中
    	枚举将下一组分成k段,每段相邻
    	枚举k段中有l段插在前面j对同组的空隙中
    	设前i组总个数为sum, 第i+1组个数为num
    	则得到转移方程
    		dp[i+1][j-l+num-k] += C(num-1, k-1) * C(j, l) * C(sum+1-j, k-l) * dp[i][j]
    	组合数什么的仔细推导下,再最后乘上每组的排列数
    */
    #include <bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int MOD = 1e9+7;
    const int N = 305;
    LL C[N][N], F[N];
    void init() {
        C[0][0] = 1;
        for (int i = 1; i < N; i++) {
            C[i][0] = C[i][i] = 1;
            for (int j = 1; j < i; j++)
                C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD;
        }
        F[0] = 1;
        for (int i = 1; i < N; i++) F[i] = i * F[i-1] % MOD;
    }
    bool check(LL a, LL b)
    {
        LL l = 1, r = 1e10, mid;
        while (l <= r)
        {
            mid = (l+r) >> 1;
            if (mid*mid <= a*b) l = mid+1;
            else r = mid-1;
        }
        return r*r == a*b;
    }
    LL dp[N][N], ans;
    int n, a[N], id[N], num[N], cnt;
    void solve()
    {
        dp[0][0] = 1;
        int sum = 0;
        for (int i = 1; i <= cnt; i++)//第i组
        {
            for (int j = 0; j <= sum; j++)//j处平方
                for (int k = 1; k <= num[i]; k++)//num[i]分成k段
                    for (int l = 0; l <= j && l <= k; l++)//j 中 l 段
                    {
                        LL tmp = dp[i-1][j];
                        tmp = tmp * C[num[i]-1][k-1] % MOD;
                        tmp = tmp * C[j][l] % MOD;
                        tmp = tmp * C[sum+1-j][k-l] % MOD;
                        dp[i][j-l+num[i]-k] += tmp;
                        dp[i][j-l+num[i]-k] %= MOD;
                    }
            sum += num[i];
        }
        ans = dp[cnt][0];
        for (int i = 1; i <= cnt; i++) ans = ans * F[num[i]] % MOD;
    }
    int main()
    {
        init();
        cnt = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            bool flag = 0;
            for (int j = 1; j < i; j++)
            {
                if (check(a[i], a[j]))
                {
                    num[id[j]]++;
                    id[i] = id[j];
                    flag = 1; break;
                }
            }
            if (!flag)
            {
                id[i] = ++cnt;
                num[cnt] = 1;
            }
        }
        solve();
        printf("%lld
    ", ans);
    }
    

      

  • 相关阅读:
    centos修改主机名 root@后面的名字
    Postgresql插入或更新操作upsert
    postgresql中使用distinct去重
    Docker permission denied while trying to connect to the Docker daemon socket
    zookeeper三节点集群安装记录
    使用Jenkins pipeline流水线构建docker镜像和发布
    使用wrk进行压力测试
    Springboot配置端口号
    intellij idea使用maven本地仓库及修改本地仓库路径
    idea 多模块项目
  • 原文地址:https://www.cnblogs.com/nicetomeetu/p/7419307.html
Copyright © 2011-2022 走看看