牛客寒假集训第六场 -牛妹的数学难题


牛妹的数学难题

思路

重点是理解连续求和的意义,以及注意ai的范围

如s(i=1,2)s(j=1,2)ai*aj,画出一个二维矩阵,分别对应(1,2),(2,2),(2,1),(1,1)三种情况,所以这里类似理解,相当于在n个数中取不同的k个数(看下标,各不相同),如果有0,贡献的答案一定是0,如果没有0仅1和2,则可以贡献正整数值。

设有a个1,b个2,若a+b

当a+b>=k 则可以搞。a中取x个1,b中取k-x个y,然后2取幂,然后累加(注意这里k-x>=0,x<=a得到a的范围是<=min(a,k) )

注意:这里对x的限制也可以移到求组合数中间去,只要限制n>=m

用到的技巧

由于需要多次取快速幂,不如直接预处理所有的2的幂次。

组合数计算需要用到逆元,这里范围是1e7,若用费马小定理,必超时

所以采用线性求逆元的方法 两种都可

1.可以在求阶乘的时候直接求出 单个数 的逆元,然后 逆元阶乘

2.或者求出 前缀积 最后一个值的逆元,然后反过来求每个前缀积的逆元,就是组合数所需要用到的。

注意初始化 inv[ 0 ]=inv[ 1 ]=1.

#include
using namespace std;
#define int long long 
int n,k;
const int N = 1e7+5;
const int mod=998244353;
int f[N];
int fv[N];
int inv[N];
int p[N];
long long qpow(int a,int n)
{
	long long base = 1;
	while(n)
	{
		if(n&1)
		{
			base=base*a%mod;
		}
		a=a*a%mod;
		n>>=1;
	}
	return base;
}
long long c(int n,int m)
{
    if(n=1;i--)
//	{
//		fv[i-1]=fv[i]*i%mod;
//	}
    for(int i=2;i<=N-1;i++)
    {
        inv[i]=inv[i-1]*inv[i]%mod;
    }
    p[0]=1;
    for(int i=1;i<=N-1;i++)
    {
        p[i]=p[i-1]*2%mod;
    }
	cin>>n>>k;
	int a=0,b=0;
	for(int i=1;i<=n;i++)
	{
		int num;
		cin>>num;
		if(num==1) a++;
		else if(num==2) b++;
	}
//	cout<