题解 [HNOI2011] 卡农

传送门

  • 有些计数题当出现形如「异或和为零」或「元素出现次数为偶数」 的限制时会有性质:
    当前 \(i-1\) 个元素确定时,第 \(i\) 个元素就随之确定了
    那么可以利用这个性质进行递推
    此时,\(f_i\) 实际上就是在不考虑上述限制条件的情况下选出 \(i-1\) 个元素的方案数

对于本题,使用上面的方法
可以先按有序计算,再除以 \(m!\)
不考虑限制条件的情况下选 \(i-1\) 个的方案数是 \(A_{2^n-1}^{i-1}\)
若第 \(i\) 个被确定为空集,方案数是 \(f_{i-1}\)
若第 \(i\) 个和之前的某一个重复,重复位置有 \(i-1\) 种选法,值有 \(2^n-1-(i-2)\) 种选法
那么可以进行一个 \(O(n)\) 递推

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
const ll mod=1e8+7;
ll A[N], inv[N], f[N], pw;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}

signed main()
{
	n=read(); m=read();
	A[0]=f[0]=1; inv[0]=inv[1]=1; pw=qpow(2, n);
	for (int i=1; i<=m; ++i) A[i]=A[i-1]*(pw-i)%mod;
	for (int i=2; i<=m; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
	for (int i=2; i<=m; ++i) inv[i]=inv[i-1]*inv[i]%mod;
	for (int i=1; i<=m; ++i) f[i]=(A[i-1]-f[i-1]-(i>=2?f[i-2]*(i-1)%mod*(pw-i+1):0))%mod;
	printf("%lld\n", (f[m]*inv[m]%mod+mod)%mod);

	return 0;
}
posted @ 2022-06-09 07:17  Administrator-09  阅读(1)  评论(0编辑  收藏  举报