(upd:随机立方体AC)
太菜了只会部分分。以后慢慢补坑吧……
随机立方体:
30分:
正常人都能想到的的转移状态(我的确是弱智),从大往小填数,记录有多少个极大值点和三个方向上各占了多少。转移可以计算。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; #define upd(x,y) x+=y,x%=P typedef long long i64; const int N=13; const int P=998244353; int f[2][N][N][N][N]; int pwr(int x,int a){ int s=1; while(a){ if(a&1)s=(i64)s*x%P; x=(i64)x*x%P; a>>=1; } return s; } int main() { int T; scanf("%d",&T); while(T--){ int r,c,l,n,m; scanf("%d%d%d%d",&r,&c,&l,&m); n=r*c*l; if(m>r&&m>c&&m>l){ printf("0 "); continue; } memset(f,0,sizeof(f)); int w=1,p=0; f[w][0][0][0][0]=1; int i,j,x,y,z; for(i=n;i;i--){ swap(w,p); memset(f[w],0,sizeof(f[w])); for(j=1;j<=n-i+1&&j<=m;j++) for(x=1;x<=n-i+1&&x<=r;x++) for(y=1;y<=n-i+1&&y<=c;y++) for(z=1;z<=n-i+1&&z<=l;z++){ upd(f[w][j][x][y][z],(i64)f[p][j][x][y][z]*max(0,x*y*z-(n-i))%P); upd(f[w][j][x][y][z],(i64)f[p][j][x-1][y][z]*(r-x+1)*y*z%P); upd(f[w][j][x][y][z],(i64)f[p][j][x][y-1][z]*x*(c-y+1)*z%P); upd(f[w][j][x][y][z],(i64)f[p][j][x][y][z-1]*x*y*(l-z+1)%P); upd(f[w][j][x][y][z],(i64)f[p][j][x][y-1][z-1]*x*(c-y+1)*(l-z+1)%P); upd(f[w][j][x][y][z],(i64)f[p][j][x-1][y][z-1]*(r-x+1)*y*(l-z+1)%P); upd(f[w][j][x][y][z],(i64)f[p][j][x-1][y-1][z]*(r-x+1)*(c-y+1)*z%P); upd(f[w][j][x][y][z],(i64)f[p][j-1][x-1][y-1][z-1]*(r-x+1)*(c-y+1)*(l-z+1)%P); } } p=1; for(i=1;i<=n;i++) p=(i64)p*i%P; printf("%d ",(i64)f[w][m][r][c][l]*pwr(p,P-2)%P); } return 0; }
AC:
容斥,钦定几个点为极大值点,其他不管,然后用组合数作为系数同时算本质相同的方案。
#include<iostream> #include<cstdio> using namespace std; typedef long long i64; const int N=5e6+6; const int P=998244353; int pwr(int x,int a){ int s=1; for(;a;a>>=1,x=1ll*x*x%P) if(a&1)s=1ll*s*x%P; return s; } int inv[N],iinv[N]; int sinv[N],siinv[N]; int v[N],f[N]; int C(int n,int m){ return 1ll*inv[n]*iinv[m]%P*iinv[n-m]%P; } int main() { int i; inv[0]=1; for(i=1;i<N;i++) inv[i]=1ll*inv[i-1]*i%P; iinv[N-1]=pwr(inv[N-1],P-2); for(i=N-1;i;i--) iinv[i-1]=1ll*iinv[i]*i%P; int T; scanf("%d",&T); while(T--){ int c,r,h,m; scanf("%d%d%d%d",&c,&r,&h,&m); int n=min(min(c,r),h); int sv=1ll*c*r%P*h%P; for(i=1;i<=n;i++) v[i]=(P+sv-1ll*(c-i)*(r-i)%P*(h-i)%P)%P; for(i=1;i<=n;i++) f[i]=1ll*C(r,i)*C(c,i)%P*C(h,i)%P*pwr(inv[i],3)%P; sinv[0]=1; for(i=1;i<=n;i++) sinv[i]=1ll*sinv[i-1]*v[i]%P; siinv[n]=pwr(sinv[n],P-2); for(i=n;i;i--) siinv[i-1]=1ll*siinv[i]*v[i]%P; int ans=0,d; for(i=m,d=1;i<=n;i++,d=P-d) (ans+=1ll*d*C(i,m)%P*f[i]%P*siinv[i]%P)%=P; printf("%d ",ans); } return 0; }
珍珠:
O(nd):
只跟奇偶性有关。
#include<iostream> #include<cstdio> using namespace std; typedef long long i64; const int P=998244353; const int S=5e7+7; const int N1=4e3+3; int f[N1][N1]; int main() { int d,n,m,i,j; scanf("%d%d%d",&d,&n,&m); if((i64)d*n<S){ f[0][0]=1; for(i=1;i<=n;i++) for(j=0;j<=d;j++){ if(j)f[i][j]+=(i64)f[i-1][j-1]*(d-j+1)%P, f[i][j]%=P; if(j!=d)f[i][j]+=(i64)f[i-1][j+1]*(j+1)%P, f[i][j]%=P; } int ans=0; for(i=0;i<=n-2*m&&i<=d;i++) ans+=f[n][i],ans%=P; printf("%d",ans); return 0; } return 0; }