[NOI2019]斗主地 [拉格朗日插值, 找规律, 组合数学]

传送门

首先有暴力的30分做法, 设 f[i][j] 为还是i, j 个的概率

f[i][j]*\frac{i}{i+j}\rightarrow f[i-1][j],f[i][j]*\frac{j}{i+j}\rightarrow f[i][j-1]

然后 v[i] 为当前的数列

v[i]=\sum_{j+k=i} f[j][k]*(a[j]*\frac{j}{i}+b[k]*\frac{k}{i})

然后你会毒瘤地发现,type = 1 时它是个等差的,type = 2时是一个二阶等差的

也就是说,我们只需要维护前3项,然后暴力插出后面的就可以了

每次分成A 和 n - A 两堆的时候,我们需要插出 A+1,A+2, A+3的值,然后类似上面的 v 解决当前的前3项

现在唯一的问题是 f 数组,显然不能 n^2 处理

发现分子和分母是定下来的

分母是  \prod _{k=i+j+1}^{n}k

分子是  \prod _{k=i+1}^{A}k*\prod _{k=j+1}^{n-A}k

然后两堆的取法是一个组合数 \binom{n-i-j}{A-i}


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;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值