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


posted @ 2022-08-08 22:44  qingyanng  阅读(92)  评论(0编辑  收藏  举报