# UOJ#214. 【UNR #1】合唱队形

PART I:

f(t) = ∑[i = 1 to 2 ^ (n - m)] g(h(i), t) * (-1) ^ (bitcount(i) + 1)

h(i)表示满足i这个状态每个人必须学会的音符个数之和

g(i, t)相当于一共有tot个数，取t次，有i个关键点，不是所有关键点都被取过的概率，仍然考虑容斥

g(i, t) = ∑[j = 1 to i] C(i, j) * (-1) ^ (bitcount(j) + 1) * ((tot - j) / tot) ^ t

PART II:

dp(i, j, k)表示当前考虑到第i个数，前m个数的状态为j，当前的h之和为k的方案数

#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fill( x, y ) memset( x, y, sizeof x )
#define copy( x, y ) memcpy( x, y, sizeof x )
using namespace std;

typedef long long LL;
typedef pair < int, int > pa;

{
int sc = 0, f = 1; char ch = getchar();
while( ch < '0' || ch > '9' ) { if( ch == '-' ) f = -1; ch = getchar(); }
while( ch >= '0' && ch <= '9' ) sc = sc * 10 + ch - '0', ch = getchar();
return sc * f;
}

const int MAXN = 35;
const int MAXM = 1005;
const int mod = 998244353;

inline void inc(int &x, int y) { x += y; while( x >= mod ) x -= mod; }
inline void dec(int &x, int y) { x -= y; while( x < 0 ) x += mod; }

int n, m, can[MAXN], tot, C[MAXM][MAXM], g[MAXM], a[MAXM], ans;
bool ok[MAXN];
char ch[MAXN];

inline int qpow(int x, int y) { int ret = 1; for( ; y ; y >>= 1, x = 1LL * x * x % mod ) if( y & 1 ) ret = 1LL * ret * x % mod; return ret; }

namespace part1
{
int b[MAXN];

inline void dfs(int st, int cur)
{
if( st == n - m + 2 )
{
if( !cur ) return ;
int cnt = 0;
for( int i = 1 ; i <= n ; i++ ) cnt += __builtin_popcount( b[ i ] );
if( cur & 1 ) dec( ans, g[ cnt ] ); else inc( ans, g[ cnt ] );
return ;
}
dfs( st + 1, cur );
if( ok[ st ] )
{
int pre[MAXN];
copy( pre, b );
for( int i = 1 ; i <= m ; i++ ) b[ st + i - 1 ] |= 1 << ch[ i ] - 'a';
dfs( st + 1, cur + 1 );
copy( b, pre );
}
}

inline void solve()
{
dfs( 1, 0 );
printf( "%d\n", ans );
}
}

namespace part2
{
int f[MAXN][1030][355], h[2055];

inline void solve()
{
for( int i = 1 ; i < ( 1 << m ) ; i++ )
{
h[ i ] = 0;
for( int j = 1 ; j <= m ; j++ ) if( i >> j - 1 & 1 ) h[ i ] |= 1 << ch[ j ] - 'a';
}
fill( f, 0 ); f[ 0 ][ 0 ][ 0 ] = 1;
int S = ( 1 << m - 1 ) - 1;
for( int i = 0 ; i < n ; i++ )
for( int j = 0 ; j <= S ; j++ )
for( int k = 0 ; k <= i * m ; k++ ) if( f[ i ][ j ][ k ] )
{
inc( f[ i + 1 ][ ( j << 1 ) & S ][ k + __builtin_popcount( h[ j << 1 ] ) ], f[ i ][ j ][ k ] );
if( i <= n - m && ok[ i + 1 ] ) dec( f[ i + 1 ][ ( j << 1 | 1 ) & S ][ k + __builtin_popcount( h[ j << 1 | 1 ] ) ], f[ i ][ j ][ k ] );
}
for( int i = 1 ; i <= tot ; i++ ) inc( ans, 1LL * f[ n ][ 0 ][ i ] * g[ i ] % mod );
printf( "%d\n", ans );
}
}

inline void solve()
{
for( int i = 1 ; i <= n ; i++ )
{
scanf( "%s", ch + 1 ); can[ i ] = 0;
for( int j = 1 ; ch[ j ] ; j++ ) can[ i ] |= 1 << ch[ j ] - 'a', tot++;
}
scanf( "%s", ch + 1 );
bool no_ans = 0;
for( int i = 1 ; i <= n - m + 1 ; i++ )
{
ok[ i ] = 1;
for( int j = 1 ; j <= m ; j++ ) if( !( can[ i + j - 1 ] & 1 << ch[ j ] - 'a' ) ) ok[ i ] = 0;
no_ans |= ok[ i ];
}
if( !no_ans ) { puts( "-1" ); return ; }
ans = 0;
for( int i = 1 ; i <= tot ; i++ ) a[ i ] = qpow( ( mod + 1 - 1LL * ( tot - i ) * qpow( tot, mod - 2 ) % mod ) % mod, mod - 2 ), g[ i ] = 0;
for( int i = 1 ; i <= tot ; i++ )
for( int j = 1 ; j <= i ; j++ )
if( j & 1 ) dec( g[ i ], 1LL * C[ i ][ j ] * a[ j ] % mod );
else inc( g[ i ], 1LL * C[ i ][ j ] * a[ j ] % mod );
if( m >= 12 ) part1::solve();
else part2::solve();
}

int main()
{
#ifdef wxh010910
freopen( "data.in", "r", stdin );
#endif
for( int i = 0 ; i <= 1000 ; i++ )
{
C[ i ][ 0 ] = 1;
for( int j = 1 ; j <= i ; j++ ) inc( C[ i ][ j ] = C[ i - 1 ][ j - 1 ], C[ i - 1 ][ j ] );
}
for( int T = read() ; T ; T-- ) solve();
return 0;
}


• 广告
• 抄袭
• 版权
• 政治
• 色情
• 无意义
• 其他

120