Easy Sum

Link

\(n^2\) 暴力很好想,但我们先找到一种常数小的 \(n^2\) 做法。

考虑组合意义,这种 \(a_i , b_i\) 的形式很容易让人联想到网格图的路径方案数。

于是令 \(f_{a_i,b_i} = 1\) 为初始状态,\(f_{i,j} = f_{i + 1 , j} + f_{i , j + 1}\)

那么 \(f_{0,k}\) 就是我们的答案。由于只有加法,常数很小,所以可以跑 40pts。

由于答案最后是分布在一列上,我们不妨用生成函数来考虑这件事。我们把一列的 dp 值写成一个生成函数,转移一列就相当于滚了一次前缀和。

滚前缀和相当于乘了一个 \(T(x) = \frac{1}{1 - x}\)(封闭形式)。

但我们还需要在滚前缀和过程中在个别位置加上 \(1\) 作为初始值。由于每个 \(T(x)\) 的长度是 \(O(n)\) 的,因此直接加很不方便。

考虑分块,设块长大小为 \(B\)。则我们每次滚 \(B\) 次前缀和,即每次乘以 \(T^B(x)\)。但这 \(B\) 列中可能还有一些计算点,这些计算点到我们新的当前列可能只需要乘以 \(T^{B - d}(x)\)

因此我们把这个点值乘以 \((1 - x)^d\) 加到我们滚 \(B\) 次前缀和的多项式上面就好了。注意到 \(d < B\) ,所以总复杂度为 \(O(\frac{n}{B} n \log n + nB)\)

所以,\(B = \sqrt{n \log n}\) 时最优。

由于是多项式题,所以常数巨大。

#include <map>
#include <set>
#include <ctime>
#include <queue>
#include <cmath>
#include <bitset>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define pii pair <int , int>
#define pll pair <LL , LL>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;

//const int Mxdt=100000; 
//static char buf[Mxdt],*p1=buf,*p2=buf;
//#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;

template <typename T>
void read(T &x) {
	T f=1;x=0;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s-'0');s=getchar();}
	x *= f;
}

template <typename T>
void write(T x , char s='\n') {
	if(!x) {putchar('0');putchar(s);return;}
	if(x<0) {putchar('-');x=-x;}
	T tmp[25]={},t=0;
	while(x) tmp[t++]=x%10,x/=10;
	while(t-->0) putchar(tmp[t]+'0');
	putchar(s); 
}

#define poly vector <int>
#define Len(x) (int)x.size()

const int MAXN = 1e5 + 5;
const int B = 1414;
//const int B = 1;
const int mod = 998244353;

inline int Add(int x , int y) {x += y;return x > mod?x - mod:x;}
inline int Mul(int x , int y) {return 1ll * x * y % mod;}
inline int Sub(int x , int y) {x -= y;return x < 0?x + mod:x;}

vector <int> P[MAXN];

int qpow(int a , int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = Mul(res , a);
		a = Mul(a , a);
		b >>= 1;
	}
	return res;
}

int r[MAXN << 2];
void NTT(poly &F , int op) {
	for (int i = 0; i < Len(F); ++i) if(i < r[i]) swap(F[i] , F[r[i]]);
	for (int k = 1 , t = 1; k < Len(F); k <<= 1 , t ++) {
		int ori = qpow(114514 , (op == 1)?(mod - 1) / (k << 1):(mod - 1) - (mod - 1) / (k << 1));
		for (int i = 0; i < (Len(F) >> t); ++i) {
			int now = 1;
			for (int j = 0; j < k; ++j) {
				int cur = Mul(F[(i << t) ^ j ^ k] , now);
				F[(i << t) ^ j ^ k] = Sub(F[(i << t) ^ j] , cur);
				F[(i << t) ^ j] = Add(F[(i << t) ^ j] , cur);
				now = Mul(now , ori);
			}
		}
	}
	if(op == -1) {
		int D = qpow(Len(F) , mod - 2);
		for (int i = 0; i < Len(F); ++i) F[i] = Mul(F[i] , D);
	}
}

poly operator * (poly F , poly G) {
	poly FG;
	int rl = Len(F) + Len(G) - 1 , n = Len(F) , m = Len(G) , N = 1;
	while(N <= rl) N <<= 1; 
	FG.resize(N) , F.resize(N) , G.resize(N);
	for (int i = 0; i < N; ++i) {
		r[i] = (r[i >> 1] >> 1) | ((i & 1)?(N >> 1):0);
		FG[i] = 0;
		if(i >= n) F[i] = 0;
		if(i >= m) G[i] = 0;
	}
	NTT(F , 1) , NTT(G , 1);
	for (int i = 0; i < N; ++i) FG[i] = Mul(F[i] , G[i]);
	NTT(FG , -1);
	FG.resize(rl);
	return FG;
}

int n , a[MAXN] , b[MAXN];
int fac[MAXN << 1] , finv[MAXN << 1];

int C(int n , int m) {
	return Mul(fac[n] , Mul(finv[n - m] , finv[m]));
}

poly Make_poly(int n , int B) {
	poly res;res.resize(n);
	for (int i = 0; i < n; ++i) res[i] = C(i + B - 1 , B - 1);
	return res;
}

poly ot , t , s[B + 5];

int main() {
	read(n);
	for (int i = 1; i <= n; ++i) read(a[i]) , read(b[i]) , P[a[i]].push_back(b[i]);
	fac[0] = 1;
	for (int i = 1; i <= n * 2; ++i) fac[i] = Mul(fac[i - 1] , i);
	finv[n * 2] = qpow(fac[n * 2] , mod - 2);
	for (int i = n * 2; i >= 1; --i) finv[i - 1] = Mul(finv[i] , i);
	
//	ot.reserve(n + 1);
//	for (int i = 0; i <= n; ++i) ot.push_back(1);
	t = Make_poly(n , B);
//	for (int i = 0; i <= n; ++i) write(t[i] , ' ');puts("");
	s[0].push_back(1);
	s[1].push_back(1) , s[1].push_back(Sub(0 , 1));
	for (int i = 2; i <= B + 1; ++i) s[i] = s[i / 2] * s[(i + 1) / 2];
	
	poly cur;cur.resize(n);
	for (int i = n - 1; i >= 0; i -= B) {
		for (int j = i; j >= max(i - B + 1 , 0); --j) {
			for (auto p:P[j]) {
				int bg = n - p - 1;
				for (int k = 0; k < Len(s[i - j]) && k + bg < n; ++k) {
					cur[k + bg] = Add(cur[k + bg] , s[i - j][k]);
				}
			}
		}
		if(i >= B) cur = cur * t;
		else cur = cur * Make_poly(n , i + 1);
		cur.resize(n);
//		for (int i = 0; i < n; ++i) write(cur[i] , ' ');puts("");
	}
	
	for (int i = n - 1; i >= 0; --i) write(cur[i] , ' ');
	
	return 0;
}
posted @ 2022-06-25 10:46  Reanap  阅读(66)  评论(0编辑  收藏  举报