2022牛客多校7 J(Melborp Elcissalc) (问题转化, dp)
https://ac.nowcoder.com/acm/contest/33192/J
题意: 长度为n的数组中ai可以填0到k-1,定义区间和modk为0的子区间是"好的",求恰好有t个好的子区间的数组a的方案数目
- 对于一个具体数组,求它有多少个好子区间有如下算法:
做模k前缀和,假设前缀为x的位置有n个,则他们贡献Cn,2个好的子区间答案就是所有种类的前缀的贡献的和 - 一个a数组对应一个模k前缀数组,那么问题可以转化为:在长度为n的数组是填0到k-1这些数字,一种数字的贡献为Cpos,2。求总贡献为t的方案。这样我们只要枚举每种数填哪几个位置(643)贡献多少(642)进行dp
- 时间复杂度5e8,转移时去掉无效转移,大概能卡到1e8内
对于题意直接进行dp可能找不出合适的状态,有时将问题转化后就找到合适的状态了
#include<bits/stdc++.h>
#include <random>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define ull unsigned long long
#define PII pair<int, int>
#define double long double
const int N = 1e2 + 5;
const int M = 3e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
const double PI = acos(-1.0);
const double eps = 1e-10;
ll f[70][70][64 * 64 + 5];
ll F[N];ll inv[N];
ll qmi( ll m, ll k ) {
ll res = 1;
while( k ) {
if ( k & 1 ) res = res * m % mod;
m = m * m % mod;
k >>= 1;
}
return res;
}
void init(int n){
F[0]=inv[0]= 1;
for(int i=1;i<=n;i++)F[i]=F[i-1]*i%mod;
inv[n]=qmi(F[n], mod - 2);
for(int i=n-1;i>=1;i--)inv[i]=inv[i+1]*(i+1)%mod;
}
ll C( ll n, ll m ) { return F[n] * inv[m] % mod * inv[n-m] % mod; }
int main () {
IOS
init(100);
int n, k, t; cin >> n >> k >> t;
for ( int i = 0; i <= n; ++ i ) {
f[0][i][i*(i + 1)/2] = C(n, i);
}
for ( int i = 1; i < k; ++ i ) {
for ( int j = 0; j <= n; ++ j ) {
for ( int x = 0; x <= j; ++ x ) {
for ( int k = x*(x-1)/2; k <= j * (j + 1) / 2; ++ k ) {
(f[i][j][k] += (f[i-1][j-x][k-x*(x-1)/2] * C( n-(j-x), x)) % mod) %= mod;
}
}
}
}
cout << f[k - 1][n][t] << '\n';
return 0;
}