P12486 题解
Wonder_Fish · · 题解
学到了一个比较好的做法,遂记录一下。
问题转化
本题要求
对
考虑从小到大枚举
特殊性质 q=0
考虑根据上述转化设计 dp,设
枚举
-
枚举
val=i 的列数k ,插入前的列数j ,然后加入这k 列。转移需要乘上一个组合数。对于每个i 这部分复杂度O(n^2) -
枚举插入完后的列数
j ,填入所有排列的i 。对f_{i,j} 乘上填i 的方案数,此时每行剩下的能填的空位数都是j-i+1 ,所以方案数是(j-i+1)^m 。这部分复杂度O(n)
最终答案为
满分做法
确定值的存在,影响了对方案数的计算。对于
下面称存在确定值的行为特殊行,列为特殊列,其余为普通行,列。
注意到
转移依然可以分成两部分:
-
插入
val=i 的列,这部分可以继续分为插入普通列和特殊列,并且这两部分相对独立,可以分开考虑。对于普通列,固定S ,其余和特殊性质的部分相似,复杂度是O(n^22^q) 。对于特殊列,固定j ,剩余部分相当于高维前缀和,可以做到O(nq2^q) 。 -
计算填入排列的方案数,同样继续分为普通行和特殊行的方案数。普通行每行空位是
j+popcnt(S)-i+1 ,特殊行需要考虑是否有特殊位置的值为i ,若有表示已经确定位置,方案数为 1;否则空位数是普通行空位数减去这行值>i 的特殊位置个数。这部分是O(nq2^q) 的。
综上,总复杂度是
Code
代码写的比较丑 QAQ
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define mod 998244353
#define N 60
#define pii pair<int,int>
#define fi first
#define sc second
#define mp make_pair
#define int long long
#define il inline
int n,m,k,h,q,a[N],b[N],cnt[1<<10],f[N][N][1<<10],c[N][N];
int s[N],t[N],v[N],d[N]; pii p[N];
il void madd(int &x,int y){
x=(x+y>=mod)?(x+y-mod):(x+y); return ;
}
il int qpow(int a,int b){
int res=1;
while(b){if(b&1) res=res*a%mod;a=a*a%mod,b>>=1;}
return res;
}
signed main(){
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
scanf("%lld%lld%lld",&n,&m,&q);
for(int i=0;i<=n;++i) c[i][0]=1;
for(int i=1;i<=n;++i)
for(int j=1;j<=i;++j) c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
for(int i=1;i<=q;++i){
scanf("%lld%lld%lld",&p[i].fi,&p[i].sc,&v[i]);
a[i]=p[i].sc,b[i]=p[i].fi,++s[v[i]];
}
sort(a+1,a+q+1),k=unique(a+1,a+q+1)-a-1;
for(int i=1;i<=q;++i){
p[i].sc=lower_bound(a+1,a+k+1,p[i].sc)-a-1;
t[v[i]]|=(1<<p[i].sc);
}
sort(b+1,b+q+1),h=unique(b+1,b+q+1)-b-1;
for(int i=1;i<=q;++i)
p[i].fi=lower_bound(b+1,b+h+1,p[i].fi)-b;
for(int S=1;S<(1<<k);++S) cnt[S]=cnt[S>>1]+(S&1);
f[0][0][0]=1;
for(int i=1;i<=n;++i){
for(int j=0;j<=n-k;++j)
for(int S=0;S<(1<<k);++S) if(f[i-1][j][S])
for(int l=0;j+l<=n-k;++l)
f[i][j+l][S]=(f[i][j+l][S]+f[i-1][j][S]*c[n-k-j][l])%mod;
for(int j=0;j<=n-k;++j)
for(int l=0;l<k;++l)
for(int S=0;S<(1<<k);++S)
if(S&(1<<l)) madd(f[i][j][S],f[i][j][S^(1<<l)]);
for(int j=0;j<=n-k;++j)
for(int S=0;S<(1<<k);++S){
if((S&t[i])!=t[i]||j+cnt[S]<i) f[i][j][S]=0;
if(!f[i][j][S]) continue;
memset(d,0,(h+1)<<3);
for(int l=1;l<=q;++l) if(S&(1<<p[l].sc)){
if(v[l]==i) d[p[l].fi]=-inf; if(v[l]>i) ++d[p[l].fi];
}
f[i][j][S]=f[i][j][S]*qpow(j+cnt[S]-i+1,m-h)%mod;
for(int l=1;l<=h;++l) if(d[l]>=0)
f[i][j][S]=f[i][j][S]*(j+cnt[S]-i+1-d[l])%mod;
}
}
printf("%lld\n",f[n][n-k][(1<<k)-1]);
return 0;
}