luogu P5279 [ZJOI2019]麻将
题面传送门
ZJLS太强啦!三年前出的题目我都做不来。足足写了5.5h才写完。
好吧其实是写了好几个假的dp
首先显然有差分,相当于对于不同的牌的张数,不能胡的组合数。具体的,设\(f_i\)为前\(i\)张牌胡不了的情况数,则答案为\(\sum {f_i(i-13)!(4n-i)!}\)
看上去像一道dp套dp的题目,我们来考虑内层dp,也就是如何判断有没有一个子集能胡。
考虑设\(dp_{i,0/1,x,y}\) 表示前\(i\)张牌,后面一位结尾的顺子有\(x\)个,后面两位结尾的顺子有\(y\)个,有\(0/1\)个对子,最大的顺子数。其中因为把三个一样的顺子换成三组三个一样显然不会更劣,因此限定\(x,y\leq 2\)。
转移就看当前这个数要不要成为对子,同时贪心一下如果剩下的个数大于等于\(3\)则还要多一个刻子。如果转移出了\(dp_{i,1,0,0}\)大于等于\(4\)就舍去。
但是问题是总共有\(5^{18}\)种状态我们不能直接压进外层dp的状态里面。
显然如果\(x1\leq x2,y1\leq y2\),则\(dp_{i,op,x1,y1}\geq dp_{i,op,x2,y2}\),因此可以差分,复杂度会降很多。但是还是不足以通过本题。
然后你写了个爆搜发现\(n\leq 100\)的时候有效的dp状态不超过\(1000\)?
所以直接压就好了,外层dp中再记录对子的个数不超过6,时间复杂度\(O(n^2|dp|+n|dp|\log n)\)
我不会告诉你我程序找到这些状态的时间比dp的时间长4倍
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define ll long long
#define db double
#define lb long db
#define N (100+5)
#define M (1000+5)
#define K ((1<<15)+5)
#define mod 998244353
#define Mod (mod-1)
#define eps (1e-9)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,m,k,x,y,z,Ct[N],Ne,La,H1,H2,Pl[5][M];ll dp[2][N<<2][M][7],Ans,frc[N<<2],Inv[N<<2];
struct Node{int F[3][3][2];bool operator <(const Node &B)const{for(int i=0;i<3;i++) for(int j=0;j<3;j++) for(int h=0;h<2;h++) if(F[i][j][h]^B.F[i][j][h])return F[i][j][h]<B.F[i][j][h];return 0;};}Ns,Tp,Cl,T1[M],T2[M];map<Node,int> P;
I int Ins(Node x){!P.count(x)&&(T2[P[x]=++H2]=x,0);return P[x];}
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
I int C(int x,int y){return frc[x]*Inv[y]%mod*Inv[x-y]%mod;}
int main(){
freopen("1.in","r",stdin);
int i,j,h,k;scanf("%d",&n);for(i=1;i<=13;i++) scanf("%d%d",&x,&y),Ct[x]++;Ne=0;La=1;Me(Cl.F,-0x3f);Ns=Cl;Ns.F[0][0][0]=0;dp[0][0][Ins(Ns)][0]=1;
for(frc[0]=Inv[0]=i=1;i<=4*n;i++) frc[i]=frc[i-1]*i%mod,Inv[i]=mpow(frc[i]);
for(i=1;i<=n;i++){P.clear();H1=H2;H2=0;swap(T1,T2);swap(Ne,La);Me(dp[Ne],0);/*cerr<<H1<<'\n';*/Me(Pl,0);
for(k=1;k<=H1;k++){
for(j=Ct[i];j<=4;j++){Me(Tp.F,-0x3f);
for(x=0;x<=2;x++){
for(y=0;y<=2;y++){
for(z=0;z<=1;z++){
if(T1[k].F[x][y][z]<0) continue;/*h=j-x-y;Tp.F[y][0][z]=max(Tp.F[y][0][z],T1[k].F[x][y][z]+(h/3));
if(h>=1) Tp.F[y][1][z]=max(Tp.F[y][1][z],T1[k].F[x][y][z]+(h/4)+1);
if(h>=2) Tp.F[y][2][z]=max(Tp.F[y][2][z],T1[k].F[x][y][z]+2),!z&&(Tp.F[y][0][1]=max(Tp.F[y][0][1],T1[k].F[x][y][z]));
if(h>=3) !z&&(Tp.F[y][1][1]=max(Tp.F[y][1][1],T1[k].F[x][y][z]+1));
if(h>=4) !z&&(Tp.F[y][2][1]=max(Tp.F[y][2][1],T1[k].F[x][y][z]+2));*/
/*Tp.F[y][j%3][z]=max(Tp.F[y][j%3][z],T1[k].F[x][y][z]+(j/3));
if(j>=1) x&&y&&(Tp.F[y-1][(j-1)%3][z]=max(Tp.F[y-1][(j-1)%3][z],T1[k].F[x][y][z]+(j/4)+1));
if(j>=2) y>=2&&x>=2&&(Tp.F[y-2][j-2][z]=max(Tp.F[y-2][j-2][z],T1[k].F[x][y][z]+2)),!z&&(Tp.F[y][j-2][1]=max(Tp.F[y][j-2][1],T1[k].F[x][y][z]));
if(j>=3) !z&&y&&x&&(Tp.F[y-1][j-3][1]=max(Tp.F[y-1][j-3][1],T1[k].F[x][y][z]+1));
if(j>=4) !z&&x>=2&&y>=2&&(Tp.F[y-2][j-4][1]=max(Tp.F[y-2][j-4][1],T1[k].F[x][y][z]+2));*/
for(h=0;h<=min(j-x-y,2);h++){
Tp.F[y][h][z]=max(Tp.F[y][h][z],T1[k].F[x][y][z]+x+(j-x-y-h)/3);
if(j-x-y-h>=2&&!z) Tp.F[y][h][1]=max(Tp.F[y][h][1],T1[k].F[x][y][z]+x);
}
}
}
}Tp.F[0][0][1]<4&&(Pl[j][k]=Ins(Tp),0);
}
}//cerr<<H2<<'\n';
for(j=0;j<=4*i-4;j++){
for(h=0;h<7;h++){
for(k=1;k<=H1;k++){if(!dp[La][j][k][h]) continue;//cerr<<i<<' '<<j<<' '<<k<<' '<<h<<' '<<dp[La][j][k][h]<<'\n';
for(x=Ct[i];x<=4;x++)/*Pl[x][k]&&(cerr<<i<<' '<<j<<' '<<k<<' '<<h<<' '<<Pl[x][k]<<' '<<x<<' '<<dp[La][j][k][h]<<'\n'),*/Pl[x][k]&&h+(x>=2)<7&&(dp[Ne][j+x][Pl[x][k]][h+(x>=2)]=(dp[Ne][j+x][Pl[x][k]][h+(x>=2)]+dp[La][j][k][h]*C(4-Ct[i],x-Ct[i]))%mod);
}
}
}
}
for(i=13;i<=4*n;i++) for(h=1;h<=H2;h++) for(j=0;j<7;j++) Ans+=dp[Ne][i][h][j]*frc[i-13]%mod*frc[4*n-i]%mod/*,dp[Ne][i][h][j]&&(printf("%d %d %d %lld\n",i,h,j,dp[Ne][i][h][j]))*/;
/*Ans-=frc[4*n-14];*/printf("%lld\n",Ans%mod*Inv[4*n-13]%mod);
}