AIZU2164

本文介绍了一种利用Burside引理解决特定排列组合问题的方法,具体涉及国家间人员排列限制以及位置连续性的条件。通过动态规划和约数的概念,实现了对符合条件方案数的高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题题意是这样的。给两个国家的人A和B,一共N个人围绕圆周坐着,其中要求来自同一个国家的不能有超过K个人的位置连续。求方案数,其中若两种方案经过旋转后相同,那么这两种方案被视为同一种方案。这里N<=1000,K<=1000。


这题思路比较显然,burside引理即可。求出绕中心旋转M个位置(即gcd(N,M))后与未旋转相同的方案数,对M从1到N求和,再除以N即可。关键是旋转M个位置后仍相同的方案数怎么求。我的思路是这样子的,首先先DP求出以A开始,以A或B收尾,长度为L的方案数有多少,这里暂时不考虑圆周首尾都是A的连接问题。接着,对于每个N的约数D,显然所求的方案数与直接求一共D个人围坐在一块的方案数是相同的。最后,对于D个人围坐一周的方案数问题,我们可以从1到K枚举与圆上选定的一个参照位置1对应的那段连续段的长度及位置1所处这段连续段的位置,即DP[1][D-P]*P。这样求和后再乘以2表示AB可以互换是对应的结果。

具体的实现见代码吧,这样说确实有点混乱的样子。

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
const ll mod=1000003;

void gcd(ll a,ll b,ll &d,ll &x,ll &y) {if(!b) {d=a;x=1;y=0;}else {gcd(b,a%b,d,y,x);y-=x*(a/b);}}
ll inv(ll a) {ll d,x,y;gcd(a,mod,d,x,y);return (x%mod+mod)%mod;}

ll gcd(ll a,ll b)
{
	if(!b) return a;
	return gcd(b,a%b);
}

ll dp[2][1005],in[1005]={0};
int n,k;

int main()
{
	int i,j,l;
	for(i=1;i<1005;i++) in[i]=inv(i);
	while(scanf("%d %d",&n,&k)!=EOF&&n)
	{
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		for(i=0;i<n;i++)
		{
			for(j=1;j<=k&&i+j<=n;j++) dp[1][j+i]+=dp[0][i],dp[1][i+j]%=mod,dp[0][j+i]+=dp[1][i],dp[0][i+j]%=mod;
		}
	//	for(i=0;i<2;i++,cout<<endl) for(j=0;j<=n;j++) cout<<dp[i][j]<<" ";
		ll res=0;
		for(i=n-1;i>=0&&i>=n-k;i--) res=(res+dp[1][i]*(n-i)%mod)%mod;
		res=res*2%mod;
		if(k>=n) res+=2,res%=mod;
	//	cout<<res<<endl;
		for(i=1;i<n;i++)
		{
			ll te=gcd(i,n);
			ll rtt=0;
			for(j=te-1;j>=0&&j>=te-k;j--) rtt=(rtt+dp[1][j]*(te-j)%mod)%mod;
			res=res+(rtt*2+2*(n<=k))%mod;
			res%=mod;
		}
	//	cout<<res<<endl;
		printf("%lld\n",(res*in[n]%mod+mod)%mod);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值