我是靠谱客的博主 糟糕身影,最近开发中收集的这篇文章主要介绍【CF840C】On the Bench-DP+组合数学,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

测试地址:On the Bench
题目大意: 给出一个长为 n n n的序列 A A A,问有多少种 1 1 1 ~ n n n的排列 p p p,满足对于任意 1 ≤ i &lt; n 1le i&lt;n 1i<n,有 A P i ⋅ A P i + 1 A_{P_i}cdot A_{P_{i+1}} APiAPi+1不为完全平方数。
做法: 本题需要用到DP+组合数学。
直接状压DP的复杂度应该是 O ( n 2 2 n ) O(n^22^n) O(n22n)的,肯定会爆,我们需要进一步发掘条件的性质。
我们发现两个数乘积为完全平方数这个性质有“传递性”,即若 a ⋅ b acdot b ab为完全平方数, b ⋅ c bcdot c bc为完全平方数,则 a ⋅ c acdot c ac也为完全平方数。证明的话,我们只需要分开来看每个质因子的幂次的奇偶性即可,根据条件, a a a c c c的各质因子的幂次应该关于模 2 2 2分别同余,而这也就意味着它们的幂次和一定为偶数,所以结论成立。
于是现在问题就简化为,有 t o t tot tot组数,共 n n n个数,要排成一排,同组数之间不能相邻,问方案数。令第 i i i组数中数的个数为 n u m i num_i numi,我们考虑一组一组进行转移。
我们先忽略它们在最后整个排列中的绝对位置,只考虑它们目前的相对位置,即不考虑空格的存在。那么令 f ( i , j ) f(i,j) f(i,j)为前 i i i组数组成的排列中,不合法的相邻元素对数有 j j j对(以下简称为不合法位置有 j j j个)的方案数,我们考虑 f ( i − 1 , j ) f(i-1,j) f(i1,j)会对 f ( i , x ) f(i,x) f(i,x)产生什么影响。
考虑第 i i i组如何摆放。首先我们把这一组的数拆成 k k k个连续段,这样这一组数内部会产生 n u m i − k num_i-k numik个不合法位置,这一步的话,拆分有 C n u m i − 1 k − 1 C_{num_i-1}^{k-1} Cnumi1k1种方案,而同组数内顺序可以互换,不会产生其他影响,因此还要乘上一个 n u m i ! num_i! numi!。拆完后,要把这 k k k段插入到序列中,此时会对原来序列中的不合法位置产生一些影响。令插入到原来序列中不合法位置的段数为 l l l,显然插入后不合法位置就会减少 l l l,而这样插入的方案数应该为: C j l ⋅ C l a s t + 1 − j k − l C_j^lcdot C_{last+1-j}^{k-l} CjlClast+1jkl l a s t last last表示前 i − 1 i-1 i1组数中元素数量之和,也就是插入前序列的长度,那么上式应该就非常明显了:在 j j j个不合法位置中选 l l l个插入,剩下的在合法位置选一些位置插入,因此就是两个组合数的乘积。于是在枚举 k , l k,l k,l的基础上,我们得到了 f ( i − 1 , j ) f(i-1,j) f(i1,j)的贡献:
f ( i − 1 , j ) ⋅ n u m i ! ⋅ C n u m i − 1 k − 1 ⋅ C j l ⋅ C l a s t + 1 − j k − l f(i-1,j)cdot num_i!cdot C_{num_i-1}^{k-1}cdot C_j^lcdot C_{last+1-j}^{k-l} f(i1,j)numi!Cnumi1k1CjlClast+1jkl
而进行完这一些操作后,不合法位置数目变为 j + n u m i − k − l j+num_i-k-l j+numikl,因此这个贡献应该累加在 f ( i , j + n u m i − k − l ) f(i,j+num_i-k-l) f(i,j+numikl)中。于是最后的答案就是 f ( t o t , 0 ) f(tot,0) f(tot,0)了。
上面的转移方程看上去是 O ( n 4 ) O(n^4) O(n4)的( i , j , k , l i,j,k,l i,j,k,l),但因为 k k k枚举的范围只到 n u m i num_i numi,因此实际上时间复杂度为 O ( n 3 ) O(n^3) O(n3),可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
int n,num[310],sum[310],tot=0,belong[310];
ll a[310],f[310][310],fac[310],C[310][310];

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		belong[i]=i;
		scanf("%lld",&a[i]); 
	}
	
	for(int i=1;i<=n;i++)
		if (belong[i]==i)
		{
			num[++tot]=1;
			for(int j=i+1;j<=n;j++)
			{
				ll s=(ll)sqrt((double)(a[i]*a[j])+0.5);
				if (s*s==a[i]*a[j]) belong[j]=i,num[tot]++;
			}
			sum[tot]=sum[tot-1]+num[tot];
		}
	
	fac[0]=1;
	for(ll i=1;i<=n;i++)
		fac[i]=fac[i-1]*i%mod;
	C[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
	
	f[0][0]=1;
	for(int i=1;i<=tot;i++)
	{
		for(int j=0;j<=max(0,sum[i-1]-1);j++)
			for(int k=1;k<=min(num[i],sum[i-1]+1);k++)
				for(int l=0;l<=min(j,k);l++)
				{
					ll nxt=f[i-1][j]*fac[num[i]]%mod;
					nxt=nxt*C[num[i]-1][k-1]%mod;
					nxt=nxt*C[j][l]%mod;
					if (k-l>sum[i-1]+1-j) continue;
					nxt=nxt*C[sum[i-1]+1-j][k-l]%mod;
					f[i][j+num[i]-k-l]=(f[i][j+num[i]-k-l]+nxt)%mod;
				}
	}
	printf("%lld",f[tot][0]);
	
	return 0;
}

最后

以上就是糟糕身影为你收集整理的【CF840C】On the Bench-DP+组合数学的全部内容,希望文章能够帮你解决【CF840C】On the Bench-DP+组合数学所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(33)

评论列表共有 0 条评论

立即
投稿
返回
顶部