T1 数数
回来补一下(n)天前的考试题.
很明显,我们通过枚举(l)与(r),可以得到一个式子:
(Ans = sum_{i = r}^{n}(n - r)! * sum_{j = 1}^{lfloor { frac{(i - 1)}{2}} floor}P^{r -2}_{i - 2})
发现这个式子的值和(l)没任何关系,然后我们想办法化简一下
(Ans = (n - r)!sum_{i = r}^n*lfloorfrac{(i - 1)}{2} floor *frac{(i - 2)!}{(i - r)!})
因为模数是(998244353),我们尽量向卷积上靠
设:
(f(i) = lfloorfrac{(i - 1)}{2} floor * (i - 2)!)
(g(i) = frac{1}{i!})
那么则有
(Ans = (n - r)!sum_{i = r}^nf(i)g(i - r))
发现貌似不能直接上卷积,我们只会套(f(i)g(r - i))这样的
那我们就设
(g'(i) = g(n - i))
那么有
(Ans = (n - r)!sum_{i = r}^nf(i)g'(n + r - i))
这样我们直接将(g')与(f)卷积,新的多项式的第(n + i)项就是(r = i)时的答案
时间复杂度(O(nlog(n)))
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<cmath>
#define LL long long
using namespace std;
const int N = 1e5 + 3;
const LL mod = 998244353;
LL a[N << 3],b[N << 3],g[N << 3];
int r[N << 3];
LL fac[N << 3],ans[N << 3];
int n,m,limit = 1,l,q;
inline LL quick(LL a,LL b){
LL res = 1;
while(b){
if(b & 1) res = res * a % mod;
b >>= 1;
a = a * a % mod;
}
return res;
}
inline void nttle(LL *A,int type){
for(int i = 0;i < limit;++i)
if(i < r[i]) swap(A[i],A[r[i]]);
for(int mid = 1;mid < limit;mid <<= 1){
LL Wn = (type == 1) ? quick(3,(mod - 1) / (mid << 1)) :
quick(3,mod - 1 - (mod - 1) / (mid << 1));
for(int R = mid << 1,j = 0;j < limit;j += R){
LL w = 1;
for(int k = 0;k < mid;++k,w = w * Wn % mod){
LL x = A[j + k],y = A[j + k + mid] * w % mod;
A[j + k] = x + y;
A[j + k + mid] = x - y;
if(A[j + k] >= mod) A[j + k] -= mod;
if(A[j + k + mid] < 0) A[j + k + mid] += mod;
}
}
}
if(type == -1){
LL inv = quick(limit,mod - 2);
for(int i = 0;i < limit;++i) A[i] = A[i] * inv % mod;
}
}
inline int read(){
int v = 0,c = 1;char ch = getchar();
while(!isdigit(ch)){
if(ch == '-') c = -1;
ch = getchar();
}
while(isdigit(ch)){
v = v * 10 + ch - 48;
ch = getchar();
}
return v * c;
}
int main(){
n = read();q = read();
fac[0] = 1;
for(int i = 1;i <= n;++i) fac[i] = fac[i - 1] * i % mod;
for(int i = 0;i <= n;++i){
a[i] = (i >= 2) ? ((i - 1) / 2) * fac[i - 2] % mod : 0;
b[i] = quick(fac[i],mod - 2);
}
reverse(b,b + n + 1);
//for(int i = 1;i <= n;++i) g[i] = b[n - i + 1];
// for(int i = 1;i <= n;++i) printf("%lld ",b[i]);puts("");
while(limit < 2 * (n + 1)) limit <<= 1,++l;
// cout << limit << endl;
for(int i = 0;i < limit;++i) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1));
//for(int i = 0;i < limit;++i) printf("%d ",r[i]);puts("");
nttle(a,1);
nttle(b,1);//for(int i = 0;i < limit;++i) printf("%lld ",b[i]);puts("");
for(int i = 0;i < limit;++i) a[i] = a[i] * b[i] % mod;
nttle(a,-1);
for(int i = 2;i <= n;++i) ans[i] = fac[n - i] * a[n + i] % mod;
while(q--){
int x = read(),y = read();
printf("%lld
",ans[y]);
}
return 0;
}