[NOI2019]斗主地 [拉格朗日插值, 找规律, 组合数学]
Posted qq62c30ac77b2a7
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2019]斗主地 [拉格朗日插值, 找规律, 组合数学]相关的知识,希望对你有一定的参考价值。
传送门
首先有暴力的30分做法, 设 f[i][j] 为还是i, j 个的概率
然后 v[i] 为当前的数列
然后你会毒瘤地发现,type = 1 时它是个等差的,type = 2时是一个二阶等差的
也就是说,我们只需要维护前3项,然后暴力插出后面的就可以了
每次分成A 和 n - A 两堆的时候,我们需要插出 A+1,A+2, A+3的值,然后类似上面的 v 解决当前的前3项
现在唯一的问题是 f 数组,显然不能 n^2 处理
发现分子和分母是定下来的
分母是
分子是
然后两堆的取法是一个组合数
30 pts (没想到矩阵快速幂啊)
#include<bits/stdc++.h>
#define N 105
using namespace std;
typedef long long ll;
int read()
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)) ch = getchar(); if(ch == -) f = -1;
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-0), ch = getchar();
return cnt * f;
const int Mod = 998244353;
int n, m, type;
ll f[N][N], v[N], a[N], b[N];
ll add(ll a, ll b) return (a+b) % Mod;
ll mul(ll a, ll b) return (a*b) % Mod;
ll power(ll a, ll b)
ll ans = 1;
for(;b;b>>=1)
if(b&1) ans = mul(ans, a);
a = mul(a, a);
return ans;
int main()
n = read(), m = read(), type = read();
for(int i=1; i<=n; i++) v[i] = (type == 1 ? i : mul(i, i));
for(int Case=1; Case<=m; Case++)
int A = read();
for(int i=1; i<=A; i++) a[i] = v[i];
for(int i=1; i<=n-A; i++) b[i] = v[i + A];
memset(f, 0, sizeof(f));
f[A][n-A] = 1;
for(int i=A; i>=0; i--)
for(int j=n-A; j>=0; j--)
ll inv = power(i + j, Mod - 2);
if(i) f[i-1][j] = add(f[i-1][j], mul(f[i][j], mul(inv, i)));
if(j) f[i][j-1] = add(f[i][j-1], mul(f[i][j], mul(inv, j)));
memset(v, 0, sizeof(v));
for(int i=n; i>=1; i--)
ll inv = power(i, Mod - 2);
for(int j=min(i, A); j>=0; j--)
int k = i - j; if(k > n - A) break;
if(j) v[i] = add(v[i], mul(f[j][k], mul(a[j], mul(inv, j))));
if(k) v[i] = add(v[i], mul(f[j][k], mul(b[k], mul(inv, k))));
int q = read();
for(int i=1; i<=q; i++)
int x = read(); printf("%lld\\n", v[x]);
return 0;
100 pts
#include<bits/stdc++.h>
#define N 10000050
using namespace std;
typedef long long ll;
int read()
int cnt = 0, f = 1; char ch = 0;
while(!isdigit(ch)) ch = getchar(); if(ch == -) f = -1;
while(isdigit(ch)) cnt = (cnt << 1) + (cnt << 3) + (ch-0), ch = getchar();
return cnt * f;
const int Mod = 998244353;
int n, m, type;
ll add(ll a, ll b) return (a+b) % Mod;
ll mul(ll a, ll b) return (a*b) % Mod;
ll power(ll a, ll b)
ll ans = 1;
for(;b;b>>=1)
if(b&1) ans = mul(ans, a);
a = mul(a, a);
return ans;
ll fac[N], inv[N];
void prework()
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for(int i=2; i<=n; i++) fac[i] = mul(fac[i-1], i);
inv[n] = power(fac[n], Mod - 2);
for(int i=n-1; i>=2; i--) inv[i] = mul(inv[i+1], i+1);
ll f[5], g[5], h[5], pre[5], suf[5];
ll C(int n, int m) return mul(fac[n], mul(inv[n-m], inv[m]));
ll Get(int A, int B, int i, int j)
if(A < i || B < j) return 0;
ll ans = power(mul(fac[A + B], inv[i + j]), Mod - 2);
ans = mul(ans, C(A + B - i - j, A - i));
ans = mul(ans, mul(mul(fac[A], inv[i]), mul(fac[B], inv[j])));
return ans;
const int up = 3;
ll lagrange(ll *y, int n)
if(n <= up) return y[n]; pre[0] = suf[up + 1] = 1;
for(int i=1; i<=up; i++) pre[i] = mul(pre[i-1], n - i);
for(int i=up; i>=1; i--) suf[i] = mul(suf[i+1], n - i);
ll ans = 0;
for(int i=1; i<=up; i++)
ll tmp = mul(y[i], mul(mul(inv[i-1], inv[up-i]), mul(pre[i-1], suf[i+1])));
ans = ((up - i) & 1) ? add(ans, Mod - tmp) : add(ans, tmp);
return ans;
int main()
freopen("landlords.in","r",stdin);
freopen("landlords.out","w",stdout);
n = read(), m = read(), type = read(); prework();
for(int i=1; i<=3; i++) f[i] = (type == 1 ? i : mul(i, i));
for(int Case = 1; Case <= m; Case ++)
int A = read();
swap(f, g);
for(int i=1; i<=3; i++) h[i] = lagrange(g, A + i);
memset(f, 0, sizeof(f));
for(int i=0; i<=3; i++)
for(int j=0; i+j <= 3; j++)
if(i + j == 0) continue;
ll tmp = Get(A, n - A, i, j);
ll inv = power(i + j, Mod - 2);
f[i + j] = add(f[i + j], mul(tmp, mul(g[i], mul(i, inv))));
f[i + j] = add(f[i + j], mul(tmp, mul(h[j], mul(j, inv))));
int q = read();
while(q--)
int x = read(); printf("%lld\\n", lagrange(f, x));
return 0;
以上是关于[NOI2019]斗主地 [拉格朗日插值, 找规律, 组合数学]的主要内容,如果未能解决你的问题,请参考以下文章