邻接表的NTT(毒瘤系数开不下二维数组)

Wan fl 20D

题意:M个Q群,每个群有si个人,每个群至少选一个,选K个人的方案数

思路:每个群挑选的生成函数为\small G(i)=\sum_{i=0}^{si }\binom{si}{i}x^{i},答案就是m个G(i)的生成函数之积后

\small x^{K}的系数   生成函数之积NTT,多个相乘,加个分治,

此题需要用邻接表。。原来的板子不适用了

#include <bits/stdc++.h>
using namespace std;
  
const int MOD = 998244353;
const int N = 401000;//大概4倍  
inline int Add(int x, int y) { x += y; if(x >= MOD) x -= MOD; return x; }
inline int Dec(int x, int y) { x -= y; if(x < 0) x += MOD; return x; }
inline int Mul(int x, int y) { return 1LL * x * y % MOD; }
inline int Kissme(int x, int y) {
  int c = 1;
  while(y) {
    if(y & 1) c = Mul(c, x);
    x = Mul(x, x);
    y >>= 1;
  }
  return c;
}

namespace FFT {   //固定板子 
	int L, l, rev[N << 1];
  
    void Pre(int n) {
        for(l = 0; (1 << l) < n; l++); L = 1 << l;
        for(int i = 0; i < L; i++) rev[i] = rev[i >> 1] >> 1 | (i & 1) << (l - 1);
    }
	void Dft(int *a) {
    	for(int i = 0; i < L; i++)
    		if(i > rev[i]) swap(a[i], a[rev[i]]);
   		for(int i = 1; i < L; i <<= 1) 
		{
       		int wn = Kissme(3, (MOD - 1) / (i << 1));//原根修改处 
        	for(int j = 0; j < L; j += i << 1)
   	    	for(int w = 1, k = 0; k < i; k++, w = Mul(w, wn)) 
     		{
        		int u = a[j + k], v = Mul(w, a[i + j + k]);
      			a[j + k] = Add(u, v);
      			a[i + j + k] = Dec(u, v);
    		}
    	}
 	}
	void Idft(int *a) {
    	reverse(a + 1, a + L); int inv = Kissme(L, MOD - 2);
    	Dft(a);
    	for(int i = 0; i < L; i++)
     		a[i] = Mul(a[i], inv);
	}
}
  
vector<int> Mul(vector<int> a, vector<int> b) {
	using namespace FFT;
	vector<int> c;
	int n = a.size() + b.size() - 1;
	if(1LL * a.size() * b.size() <= (a.size() + b.size()) * 20LL) {
    	c.resize(n);
    	for(int i = 0; i < a.size(); i++)
      		for(int j = 0; j < b.size(); j++)
    			c[i + j] = Add(c[i + j], Mul(a[i], b[j]));
        return c;
  	}
    Pre(n);//固定操作  更新L 
    c.resize(L); a.resize(L); b.resize(L);
    Dft(&a[0]); Dft(&b[0]);
    for(int i = 0; i < L; i++)
        c[i] = Mul(a[i], b[i]);
    Idft(&c[0]);
    c.resize(n);
    return c;
}
  
int fac[N], inv[N], fa[N], siz[N], n, K, m;
vector<vector<int> > All;
vector<int> T[N << 2];
  
void Init(void) {//预处理阶乘 
	fac[0] = 1; int lim = 100001;
	for(int i = 1; i <= lim; i++) fac[i] = Mul(fac[i - 1], i);
  	inv[lim] = Kissme(fac[lim], MOD - 2);
  	for(int i = lim - 1; i >= 0; i--) inv[i] = Mul(inv[i + 1], i + 1);//乘 
}
int C(int x, int y) {
  	if(y > x) return 0;
  	return Mul(fac[x], Mul(inv[y], inv[x - y]));
}
void Solve(int o, int l, int r) {//分治 多个多项式相乘 
  	if(l == r) return(void)(T[o] = All[l]);
  	int mid = l + r >> 1;
  	Solve(o << 1, l, mid);
  	Solve(o << 1 | 1, mid + 1, r);
  	T[o] = Mul(T[o << 1], T[o << 1 | 1]);
}
  
int main(void) {
  	Init();
  	scanf("%d%d%d", &n, &m, &K);
  	All.clear();
  	for(int i = 1; i <= m; i++) 
	{
   		vector<int> x;//第i个多项式的系数 
   		x.push_back(0);
   		int y;
    	scanf("%d", &y);
   		for(int j = 1; j <= y; j++)
       		x.push_back(C(y, j));
    	All.push_back(x);//ALL就是所有多项式的系数集合 
 	}
  	Solve(1, 0, All.size() - 1);//一共all.size个多项式 
  	printf("%d\n", T[1][K]);//x^K的系数 
  	return 0;
}

猜你喜欢

转载自blog.csdn.net/animalcoder/article/details/81272169