排列组合,容斥原理,lucas,中国剩余

一直认为,天下最苦莫过于调代码

的教训,放这里:1.C(a,b),当a<b return 0      否则inv[a-b]会访问非法空间

 

 

 

模型一:站队问题

元素优先法:特殊位置优先讨论

相邻捆绑法:小团体也是

定序按照无序算,或者看成其他元素确定,定序对方案数量没有影响

模型二:排队

插空法:

三个人8座位,每个人左右有空位:

空位5个,空间隔4个,三个人插里面,三个人各自有位置顺序

模型三:排数

注意0特殊位置,计算方案数要用位置(第一个位置放什么)

不能用数字(这个数字可以放什么位置),这样就混了

 

 

 排列组合算法

C combine

求有多少种长度为 n 的序列 A,满足以下条件: 1 ~ n 这 n 个数在序列中各出现了一次 若第 i 个数 A[i] 的值为 i,则称 i 是稳定的。序列恰好有 m 个数是稳定的 满足条件的序列可能很多,序列数对 10^9+7 取模。

const int N=1000000+10;
ll inv[N],mul[N],mul_inv[N],d[N];
int t,n,m;
void pre()
{
    mul[0]=mul_inv[0]=inv[0]=1;
    inv[1]=mul[1]=mul_inv[1]=1;
    _f(i,2,N)
    {
        mul[i]=mul[i-1]*i%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        mul_inv[i]=mul_inv[i-1]*inv[i]%mod;
        //if(i<=4)chu("mulinv %d %lld\n",i,mul_inv[i]);
    }
    d[0]=1,d[1]=0,d[2]=1;
    _f(i,2,N)
    {
        d[i]=(i-1)*((d[i-1]+d[i-2])%mod)%mod;
    }
}
int main()
{
     t=re();
     pre();
     for(int i=1;i<=t;i++)
     {
         n=re(),m=re();
     //    chu("%lld %lld %lld %lld\n",mul_inv[n-m],mul_inv[m],mul[n],d[n-m]);
         chu("%lld\n",(((mul_inv[n-m]*mul[n])%mod*mul_inv[m])%mod*d[n-m]%mod)); 
     }

    return 0;
}
/*
预处理:线性求组合数(就是帅!),线性求逆元,线性求错位排序 
*/

 D

称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值

还可以结合小根堆大根堆求方案数量!

f[i]=(f[i*2]*f[i*2+1]*C(siz[i],siz[i*2])) %p

综合各种方法吧,p一定是质数

数组下标二倍!细心啊

 

容斥原理

//多步容斥原理 ,在组合数ppt里,一堆加减的公式那个
int f[N]={1};
int c[4],d[4];
int ans;
//这里非常神奇啊
//就是对于递归到的每一个硬币,
/*
如果k=奇数,代表前面已经超限了奇数个,是应该减去的
如果算上这个硬币超限,就是偶数个,应该加,所以k+1,奇数变偶数
如果不算上,还是奇数个,递归传递下去
因为每一个硬币只有超限或不超限制两种情况,所以dfs可以覆盖所有 
当然笨拙一点也可以 
*/ 
void dfs(int x,int k,int now)
{
	if(now<0)return;//没钱了结束了
	if(x==4)// 
	{
		ans+=k&1?-f[now]:f[now];//
		return; 
	 } 
	 dfs(x+1,k+1,now-c[x]*(d[x]+1));//
	 dfs(x+1,k,now); 
}
int main()
{
	int i,j,t,s;
	
	_f(i,0,3)
	{
		_f(j,c[i],10000)
		{
			f[j]+=f[j-c[i]];//这个dp算方案数量的 
		}
	 } 
	//所有可能方案-一个超限的不合法硬币方案,减去重复了,再加上两两重复的,循环到最多个 
	 while(t--)
	 {
	 	_f(i,0,3)
	 	d[i]=re();
	 	s=re();
	 	ans=0;
	 	dfs(0,0,s);
	 	chu("%d",ans);
	 
	 }
    return 0;
}

  E集合计数

一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)

容斥原理,交集元素个数是k,把k固定,剩下(n-k)个元素有a=2^(n-k)集合数,从中任意取,每个集合都可以取或者不,

但不能都不取出来,因此有2^a-1种取法,C(n,k)*(2^a-1)是答案?no!

还要去除交集是k+1,k+2,k+3.....的方案数,+(所有交集是k+1)的方案数-(所有交集是k+2)的方案数

这里就看成在n-k个元素的基础上进行就可以,C(n-k,i)*(取i个方案数)

二的次幂怎么办?f(i)=2^(2^i)-1,发现f(i)=f(i-1)*f(i-1)+f(i-1)*2;f[0]=1

线性递推解决吧!

F. 牡牛和牝牛

排列组合,可重复计数原理:
当于 0 1序列排序,每个0之间必须有至少k个1
先固定找出必须的1,设有x个0(0决定了1),至少有k(x-1)个1
剩下n-k(x-1)-x个必须是1了
a=x+1个0的空隙里插入b=n-k(x-1)-x个1
抽象!
b个无序球放入a个有序盒子里
法一:从a+b-1个数字里挑出b个
C(a+b-1,b)
法二:隔板法
b个球插入a-1个隔板,a+b-1个位置插入b个隔板
C(a+b-1,b)

排列组合、crt、费马小定理,容斥原理,lucas定理,组合数取模 - 比赛 - 衡中OI (hszxoj.com)

G.小小的优化

1 for example
2 _f(i,1,n)
3 {ans+=C(t+i,t);}
4 printf("%d",ans);
5 当n>=1e6基本超时
6 把原式子+C(n+1,n+17 C(t+1,t+1)+C(t+1,t)=C(t+2,t+1)
8 C(t+2,t+1)+C(t+2,t)=C(t+3,t+1)
9 得到答案是C(t+n+1,t+1)

 J

1-n的数字,问有几种排列方法,使得呈现山峰排布,大小大小大小大(相对左边右边)

dp结合排列组合思想

f[i]【j】表示选取前i个数,j个不合法元素的方案数量

分类讨论,计算个数

const int N=4200+10;
int n;ll p;
ll f[4][N];
int main()
{
    n=re();scanf("%lld",&p);
    f[1][0]=4LL,f[1][1]=2LL;
    _f(i,4,n)//有多少个元素
    {
        _f(j,0,i-2)
        f[0][j]=f[1][j],f[1][j]=0;
        _f(j,0,n-3)//有多少个非法·元素
        {
            f[1][j]=(f[1][j]+f[0][j]*2)%p;//i-1贡献2个
            if(j)//如果非法>0
                f[1][j-1]=(f[1][j-1]+f[0][j]*j)%p;
            f[1][j+1]=(f[1][j+1]+(i-j-2)*f[0][j])%p;    
        } 
     } 
chu("%lld",f[1][0]);
    return 0;
}

 H

构造环解决合法方案数统计问题

n个人占k个位置,每个人选择座位随机,如果这个位置有人了,可以往后坐,不能往前,问每个人都有位置的概率是多少

ans=(每个人都有位置方案数)/n^k

怎么找?

首先想到我统计每个人都有位置方案数,先看成每个人没有区别,转化成球和盒子问题,dp递推解决

然后发现无法转化成有序

 

算出合法的排列数量
怎么合法呢?
以k-f为界限:
num_total[p]<=k-p+1//向下求和,每一层都要满足
从下向上递推?n_t[k]=1/0 [2]
n_t[k-1]=0/1/2	[3]
n_t[k-2]=0/1/2/3	[4]		
f[i][j]代表截至i层一共放j个人的方案数量
f[i][j]=ceita(f[i+1][k])//0<=k<=i 
j层放0个或者j个(最多了)		
f[1][n]就是所有合法方案数 
tot_ok==f_tot*A(n,n) 总的合法方案数量
 ans=f_tot/(k^n) 
 怎么统计出来当人是不一样的合法方案数?(在一个盒子里算成一种,直接乘以全排列) 
 求出了n k-->有区别n k怎么转化???

 

  悲惨结束

我在最后一个位置加上一个座位,把k和1位置连起来,总方案数(k+1)^n

保证了一定合法,但是我抽象出这种排列,1234 2341是一样的所以/(k+1)

想象对于任意一种我够早的环,即使有多人选择同一个座位,我也可以把这些i人顺次展开,那么最后一定剩下k+1-n个座位,就是那把空椅子,把它切掉断环不影响合法性,这时候环头和尾巴代表座位次序

每个人都有位置

ans=(k+1)^n/k^n*(k-n+1)

 

 

 

posted on 2022-05-09 08:31  HZOI-曹蓉  阅读(133)  评论(0)    收藏  举报