• 【详细揭秘】多重集の交错排列


    1. 题面

    一个多重集,问相邻元素不相同的排列有多少个 .

    2. 解法

    Step 1. 容斥

    为了方便,我们令这个多重集 (M)(m) 中不同元素 (a_1,a_2,cdots,a_m),并且各出现 (n_1,n_2,cdots,n_m) 次 .

    考虑容斥,于是问题变成求至少有 (t) 个同种元素构成的方案数 .

    将「打破 (t) 个约束」看作「合并 (t) 个集合」的过程,即用捆绑法每次将两个元素合并成一个元素,那么合并之后看成多重集全排列即可 .

    于是答案就是

    [sum_{b_{1cdots,m}\,,0le b_i<n_i}dfrac{(sum n_i-b_i)!}{prod(n_i-b_i)!}prod_{i=1}^m(-1)^{b_i}dbinom{n_i-1}{b_i} ]

    Step 2. dp

    转而枚举 (n_i-b_i)(下式中写为 (c_i)),则柿子变为

    [sum_{c_{1cdots,m}\,,1le c_ile n_i}dfrac{(sum c_i)!}{prod c_i!}prod_{i=1}^m(-1)^{n_i-c_i}dbinom{n_i-1}{n_i-c_i} ]

    再令 (s=sum c_i),则柿子化成

    [sum_{c_{1cdots,m}\,,1le c_ile n_i}(-1)^{n-s}dfrac{s!}{prod c_i!}prod_{i=1}^mdbinom{n_i-1}{n_i-c_i} ]

    这个容斥系数 ((-1)^{n-s})(s!) 都解决掉了,考虑计算后面的

    [dfrac{1}{prod c_i!}prod_{i=1}^mdbinom{n_i-1}{n_i-c_i} ]

    我们枚举一个 (sum c_i),这样 (c_i) 就有俩限制,于是可以背包 .

    (dp_{i,j}) 为考虑前 (i) 个元素,且 (sum c_i=j) 的答案,则

    [dp_{i,j}=sum_{k=1}^{min(n_i,j)}dp_{i-1,j-k}cdot dfrac1{k!}dbinom{n_i-1}{n_i-k} ]

    非常标准的背包转移 .

    预处理组合数,则可以 (O(n^2)) 求解 .

    3. [AHOI2018初中组]球球的排列

    1. 题面

    链接:https://www.luogu.com.cn/problem/P4448

    给一个序列 ({a}),问有多少个排列 ({p}) 满足对于所有 (2le i<n),有 (a_{p_i-1}cdot a_{p_i+1}) 不为完全平方数 .

    数据范围:(a_ile 10^9,\,nle 300) .

    2. 解法

    显然「完全平方」性质是有传递性的,即如果 (xy,yz) 均为完全平方数,则 (xz) 为完全平方数 .

    所以说,就转换成了上面那个问题,但是要带标号 .

    只需要把以前的柿子乘个阶乘即可((prod) 里面多乘个 (n_i!),显然 dp 转移方程里也该乘 (n_i!)).

    这个题的代码:

    using namespace std;
    #define int long long // 去掉就 WA 一部分 qwq,我也不知道哪里忘改 long long 了 :( 
    typedef long long ll;
    const int N=333,MOD=1e9+7;
    int n;
    ll a[N],r[N],sz[N],dp[N],tdp[N],tmp[N],fac[N],ifac[N],inv[N],ans;
    template<typename T>
    inline T sqr(const T& t){return t*t;}
    bool issqr(ll a){return sqr((int)(sqrt(a)))==a;}
    ll C(int m,int n){return (m<n)?0:fac[m]*ifac[n]%MOD*ifac[m-n]%MOD;}
    void initC(int n)
    {
    	fac[0]=ifac[0]=fac[1]=ifac[1]=inv[1]=1;
    	for (int i=2;i<=n;i++)
    	{
    		fac[i]=fac[i-1]*i%MOD;
    		inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;
    		ifac[i]=ifac[i-1]*inv[i]%MOD;
    	}
    }
    signed main()
    {
    	scanf("%lld",&n); initC(n);
    	for (int i=1;i<=n;i++) scanf("%lld",a+i);
    	for (int i=1;i<=n;i++) r[i]=i;
    	for (int i=1;i<=n;i++)
    	{
    		++sz[r[i]];
    		for (int j=i+1;j<=n;j++)
    			if (issqr(a[i]*a[j])) r[j]=r[i];
    	}
    	dp[0]=1; ll tot=0;
    	for (int i=1;i<=n;i++)
    	{
    		if (!sz[i]) continue;
    		for (int j=0;j<=tot;j++){tmp[j]=dp[j]; dp[j]=0;}
    		for (int j=0;j<sz[i];j++)
    		{
    			tdp[j]=ifac[sz[i]-j]*C(sz[i]-1,j)%MOD;
    			if (j&1) tdp[j]=MOD-tdp[j];
    			for (int k=0;k<=tot;k++) dp[j+k]=(dp[j+k]+tdp[j]*tmp[k])%MOD;
    		} tot+=sz[i]-1;
    	}
    	for (int i=0;i<=tot;i++) ans=(ans+dp[i]*fac[n-i])%MOD;
    	for (int i=1;i<=n;i++) ans=ans*fac[sz[i]]%MOD;
    	printf("%lld
    ",ans);
    	return 0;
    }
    // 参考 https://www.luogu.com.cn/blog/skydogli/solution-p4448
    
  • 相关阅读:
    PL/SQL Developer连接Oracle
    Oracle 11g 监听命令
    Oracle 11g的登陆问题
    PL/SQL Developer 配置和使用
    KMP应用求两个字符串的最长公共子串
    msc pool概念
    nformix调优之执行计划取得
    lsof 与fuser
    informix onstat命令收集
    各类系统上查看占cpu最多的进程
  • 原文地址:https://www.cnblogs.com/CDOI-24374/p/15149546.html
Copyright © 2020-2023  润新知