HNOI2011 卡农

讲个笑话:我的做法是 O(nlog n)

Description

link

有对于一个给定的集合 S=1...n ,选取其中 m 个子集要求子集非空,不能相同,并且每个出现过的元素出现次数是偶数

n,m106

Solution

这个题要求组合的方案,先转化一步:求排列的方案然后除以阶乘

状态:fi 为取 i 个集合的方案

之后发现一个性质:我们确定前 i1 个集合,最后一个就被自然确定了

(补全原来没有拿成偶数的元素即可)

这样我们发现取 m 个集合的方案是:A2n1i1

这样我们发现可能不太对

1. 最后一个集合是空集,即原来取的方案已经满足所有的元素出现的个数为偶数

解决方案:直接f[i]=f[i1];

2. 最后一个集合和原来的集合是重复的

最后一个集合可能和原来的 i1 个的重复,此时我们拿出那两个重复的集合,还剩余 i2

i2 的方案是有 fi2

重复的集合的可能有sum(2n1)exist(i2) 括号里的是真值

理解就是总的个数和最后剩下没有重复的个数

重复集合的位置有 i1 种(有序计数)

预处理阶乘逆元和排列数即可

讲述前面的那个笑话:考虑到我闲的就把 2n 求了 n

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
namespace yspm{
	inline int read()
	{
		int res=0,f=1; char k;
		while(!isdigit(k=getchar())) if(k=='-') f=-1;
		while(isdigit(k)) res=res*10+k-'0',k=getchar();
		return res*f;
	}
	const int N=1e6+10,mod=1e8+7;
	int inv,p,f[N],n,m,a[N];
	inline int ksm(int x,int y)
	{
		int res=1; for(;y;y>>=1,(x*=x)%=mod) if(y&1) (res*=x)%=mod;
		return res;
	}
	int num;
	signed main()
	{
		n=read(); m=read(); inv=1;
		for(int i=1;i<=m;++i) inv*=i,inv%=mod; inv=ksm(inv,mod-2);
		a[0]=1; for(int i=1;i<=m;++i) a[i]=a[i-1]*((ksm(2,n)-i+mod)%mod)%mod;
		f[0]=1;
		for(int i=2;i<=m;++i)
		{
			f[i]=(a[i-1]-f[i-1]+mod)%mod; 
			f[i]-=f[i-2]*(i-1)%mod*((ksm(2,n)-1-(i-2)+mod)%mod)%mod;
			(f[i]+=mod)%=mod;
		}
		cout<<f[m]*inv%mod<<endl;
		return 0;
	}
}
signed main(){return yspm::main();}

Review

组合计数不好求的时候可以考虑转排列求阶乘

再一个就是数学的分类讨论一定要细致求好

posted @   没学完四大礼包不改名  阅读(119)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示