列表

详情


NC20089. [HNOI2011]卡农

描述

众所周知卡农是一种复调音乐的写作技法,小余在听卡农音乐时灵感大发,发明了一种新的音乐谱写规则。他将声音分成 n 个音阶,并将音乐分成若干个片段。音乐的每个片段都是由 1 到 n 个音阶构成的和声,即从 n 个音阶中挑选若干个音阶同时演奏出来。为了强调与卡农的不同,他规定任意两个片段所包含的音阶集合都不同。同时为了保持音乐的规律性,他还规定在一段音乐中每个音阶被奏响的次数为偶数。现在的问题是:小余想知道包含 m 个片段的音乐一共有多少种。两段音乐 a 和 b 同种当且仅当将 a 的片段重新排列后可以得到 b。例如:假设 a为{{1,2},{2,3}},b 为{{3,2},{2,1}},那么 a 与 b 就是同种音乐。由于种数很多,你只需要输出答案模 100000007(质数)的结果。

输入描述

从文件input.txt中读入数据,输入文件仅一行,具体是用空格隔开的两个正整数n和m,分别表示音阶的数量和音乐中的片段数。
20%的数据满足n,m≤5,50%的数据满足n,m≤3000,100%的数据满足n,m≤1000000。

输出描述

输出文件 output.txt 仅包含一个非负整数,表示音乐的种数模 100000007 的结果。

示例1

输入:

2 3

输出:

1

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 54ms, 内存消耗: 16096K, 提交时间: 2019-08-20 21:58:20

#include<bits/stdc++.h>

#define FOG(x,y,z) for(register int x=y,x##_=z;x<=x##_;++x)
#define DOG(x,y,z) for(register int x=y,x##_=z;x>=x##_;--x)
#define FOR(x,y,z) for(int x=y,x##_=z;x<=x##_;++x)
#define DOR(x,y,z) for(int x=y,x##_=z;x>=x##_;--x)
#define FOR_(x,y,z,s) for(int x=y,x##_=z;x<=x##_;x+=s)
#define FOR__(x,y,z) for(int x=y,x##_=z;x<=x##_;x<<=1)
#define EOR(x,y) for(int x##_=head[x],y=edge[x##_].e;x##_;y=edge[x##_=edge[x##_].to].e)
#define EOD(x,y,z) for(int &x##_=head[x],y=edge[x##_].e,z=edge[x##_].c;x##_;y=edge[x##_=edge[x##_].to].e,z=edge[x##_].c)
#define While(x) for(;x;)
#define clr(x,y) memset(x,y,sizeof(x))
#define szf(x) sizeof(x)
#define min3(x,y,z) min(min(x,y),z)
#define max3(x,y,z) max(max(x,y),z)

#define read2(x,y) read(x),read(y)
#define read3(x,y,z) read(x),read(y),read(z)
#define read4(x,y,z,w) read3(x,y,z),read(w)
#define reads(str) sf("%s",str)

#define ts (*this)
#define sf scanf
#define pf printf

#define ll long long
#define ull unsigned long long
#define db double
#define ct clock_t
#define ck() clock()
#define rd rand()
#define rmx RAND_MAX
#define RD T*(rd*2-rmx)


using namespace std;

template<class T>bool tomin(T &x,T y){return y<x?x=y,1:0;}
template<class T>bool tomax(T &x,T y){return x<y?x=y,1:0;}
template<class T>void read(T &x){
	char c;
	x=0;
	int f=1;
	while(c=getchar(),c<'0'||c>'9')if(c=='-')f=-1;
	do x=(x<<3)+(x<<1)+(c^48);
	while(c=getchar(),c>='0'&&c<='9');
	x*=f;
}
bool mem1;
const db Pi=acos(-1);
const int P=100000007;
const int maxn=1e6+5;
int n,m;
namespace P20{
	int ans;
	void dfs(int x,int s,int p){
		if(x==m+1){
			ans+=s==0;
			return;
		}
		FOR(i,p,n-1)dfs(x+1,s^i,i+1);
	}
	void solve(){
		n=1<<n;
		dfs(1,0,1);
		pf("%d",ans);
	}
}
namespace P100{
	ll inv[maxn],pw;
	ll A,im;
	void Init(){
		pw=1;FOR(i,1,n)pw=(pw+pw)%P;
		A=pw=(pw-1+P)%P;
		im=inv[1]=1;FOR(i,2,m)im=im*(inv[i]=(P-P/i*inv[P%i]%P)%P)%P;
	}
	ll dp[maxn];
	void solve(){
		Init();
		dp[0]=1;
		dp[1]=0;
		FOR(i,2,m){
			dp[i]=(A-dp[i-1]+P)%P;
			dp[i]=(dp[i]-dp[i-2]*(i-1)%P*(pw-i+2)%P+P)%P;
			A=A*(pw-i+1)%P;
		}
		dp[m]=dp[m]*im%P;
		pf("%lld",dp[m]);
	}
}
int main(){
	// cerr<<(&mem2-&mem1)/1024.0/1024<<endl;
	srand(time(NULL));
	read2(n,m);
	if(n<=5)P20::solve();
	else P100::solve();
	return 0;
}

C++11(clang++ 3.9) 解法, 执行用时: 43ms, 内存消耗: 16096K, 提交时间: 2020-04-01 22:41:20

#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const ll mod=100000007;
ll n,m,ans=1;
ll fac[1000005],res[1000005];
inline ll ksm(ll x,ll k)
{
	if(k==1) return x;
	ll tmp=ksm(x,k>>1);
	if(k&1) return (tmp*tmp%mod)*x%mod;
	return tmp*tmp%mod;
}
int main()
{
	int i,j;
	scanf("%lld%lld",&n,&m);
	ll czy=ksm(2,n);
	czy=(czy+mod)%mod;
	fo(i,1,m-1)
	{
		ans=(ans*(czy-i))%mod;
		res[i+1]=((ans-res[i])%mod+mod)%mod;
		res[i+1]=(res[i+1]-(res[i-1]*(czy-i)%mod)*i%mod+mod)%mod;
		res[i+1]=(res[i+1]%mod+mod)%mod;
		res[1]=res[2]=0;
	}
	fac[0]=1;
	fo(i,1,m) fac[i]=fac[i-1]*i%mod;
	ans=(res[m]*ksm(fac[m],mod-2))%mod;
	printf("%lld\n",(ans+mod)%mod);
	return 0;
}

上一题