2025多校冲刺省选模拟赛14
2025多校冲刺省选模拟赛14
\(T1\) A. 游戏 \(0pts\)
-
同 初中信息奥赛模拟测试 T4 ZEW 玩 thd ,考虑将攻击次数攒起来,仅通过其对应的随从死亡状态推出贡献。
-
考虑状压,设 \(f_{i,S}\) 表示一共攻击了 \(i\) 次,随从死亡状态为 \(S\) 时用来攻击随从的操作的贡献。死亡人数不变时直接继承 \(\times \frac{1}{|S|}\) 即可,死亡人数加一时在前面任选 \(a_{x}-1\) 步乘以组合数即可。
-
然后考虑 \(U\) \ \(S\) 时的方案数统计,背包预处理的时间复杂度为 \(O(2^{n}m^{2})\) ,但跑不过因子集和很难卡满所以每次重新计算的 \(O(2^{n}nm^{2})\) ,可以使用 NTT/FFT 优化至 \(O(2^{n}m \log m)\) 。
点击查看预处理代码
const int p=998244353; int a[210],inv[210],jc_inv[210],C[210][210],sum[(1<<15)+10],f[2][(1<<15)+10],g[210][(1<<15)+10]; int lowbit(int x) { return (x&(-x)); } int main() { #define Isaac #ifdef Isaac freopen("game.in","r",stdin); freopen("game.out","w",stdout); #endif int n,m,ans=0,s,i,j; cin>>n>>m; for(i=1;i<=n;i++) cin>>a[i]; inv[1]=C[0][0]=C[1][0]=C[1][1]=1; for(i=2;i<=m;i++) { inv[i]=1ll*(p-p/i)*inv[p%i]%p; C[i][0]=1; for(j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%p; } for(s=0;s<(1<<n);s++) { for(i=0;i<n;i++) { if((s>>i)&1) sum[s]+=a[i+1]; } } f[0][0]=1; for(i=0;i<m;i++) { memset(f[(i+1)&1],0,sizeof(f[(i+1)&1])); for(s=0;s<(1<<n);s++) { if(i-sum[s]<0) continue; f[(i+1)&1][s]=(f[(i+1)&1][s]+1ll*f[i&1][s]*inv[n-__builtin_popcount(s)]%p)%p; for(j=0;j<n;j++) { if(((s>>j)&1)==0&&i-sum[s]>=a[j+1]-1) f[(i+1)&1][s|(1<<j)]=(f[(i+1)&1][s|(1<<j)]+ 1ll*f[i&1][s]*inv[n-__builtin_popcount(s)]%p*C[i-sum[s]][a[j+1]-1]%p)%p; } } } for(s=0;s<(1<<n);s++) g[0][s]=1; for(s=1;s<(1<<n);s++) { for(i=1;i<=m;i++) { for(j=0;j<=min(i,a[__builtin_ctz(s)+1]-1);j++) g[i][s]=(g[i][s]+1ll*g[i-j][s^lowbit(s)]*C[i][j]%p)%p; } } for(s=0;s<(1<<n);s++) { if(m-sum[s]>=0) ans=(ans+1ll*f[m&1][s]*g[m-sum[s]][((1<<n)-1)^s]%p*__builtin_popcount(s)%p)%p; } cout<<ans<<endl; return 0; }
点击查看重新计算代码
const ll p=998244353; ll a[30],jc[(1<<15)+10],inv[(1<<15)+10],jc_inv[(1<<15)+10],sum[(1<<15)+10],f[2][(1<<15)+10],g[210]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) ans=ans*a%p; b>>=1; a=a*a%p; } return ans; } ll C(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?jc[n]*jc_inv[n-m]%p*jc_inv[m]%p:0; } int main() { #define Isaac #ifdef Isaac freopen("game.in","r",stdin); freopen("game.out","w",stdout); #endif ll n,m,k,ans=0,s,i,j; cin>>n>>m; jc[0]=jc_inv[0]=1; for(i=1;i<=n;i++) cin>>a[i]; for(i=1;i<=max(n,m);i++) { inv[i]=qpow(i,p-2,p); jc[i]=jc[i-1]*i%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } for(s=0;s<(1<<n);s++) { for(i=0;i<n;i++) { if((s>>i)&1) sum[s]+=a[i+1]; } } f[0][0]=1; for(i=0;i<m;i++) { memset(f[(i+1)&1],0,sizeof(f[(i+1)&1])); for(s=0;s<(1<<n);s++) { if(i-sum[s]<0) continue; f[(i+1)&1][s]=(f[(i+1)&1][s]+f[i&1][s]*inv[n-__builtin_popcount(s)]%p)%p; for(j=0;j<n;j++) { if(((s>>j)&1)==0&&i-sum[s]>=a[j+1]-1) f[(i+1)&1][s|(1<<j)]=(f[(i+1)&1][s|(1<<j)]+ f[i&1][s]*inv[n-__builtin_popcount(s)]%p*C(i-sum[s],a[j+1]-1,p)%p)%p; } } } for(s=0;s<(1<<n);s++) { if(m-sum[s]<0) continue; memset(g,0,sizeof(g)); g[0]=1; for(i=0;i<n;i++) { if(((s>>i)&1)==0) { for(j=m-sum[s];j>=1;j--) { for(k=1;k<=min(j,a[i+1]-1);k++) g[j]=(g[j]+g[j-k]*C(j,k,p)%p)%p; } } } ans=(ans+f[m&1][s]*g[m-sum[s]]%p*__builtin_popcount(s)%p)%p; } cout<<ans<<endl; return 0; }
\(T2\) B. 排序 \(40pts\)
-
手模下较小数据下操作 \(1,2\) 的本质是将某个非前缀 \(\max\) 的数 \(p_{i}\) 消耗 \(2^{x}\) 的次数使得将 \(p_{i}\) 插入到其在 \([1,i]\) 升序排序后的位置,其中 \(x\) 表示 \([1,i-1]\) 中比 \(x\) 小的数的个数。
-
考虑按照 \(k\) 的二进制表示下从低到高的各位进行构造。
-
以第 \(0\) 位为例,若 \(k\) 这一位为 \(0\) 那么此时必须放 \(1\) ;否则必须放 \(2\) ,但后续过程中 \(1\) 所在的位置就不确定了。
-
\(1\) 对其他数的影响本质上是将其的贡献 \(\times 2\) ,故 \(k\) 某一位为 \(0\) 时可以将 \(1\) 塞进去从而进行拼接。
点击查看代码
stack<ll>s; int main() { #define Isaac #ifdef Isaac freopen("sorting.in","r",stdin); freopen("sorting.out","w",stdout); #endif ll k; cin>>k; for(ll x=1;k;k>>=1) { if(k%2==1) { s.push(x); x++; cout<<x<<" "; x++; } else { if(s.empty()==0) { cout<<s.top()<<" "; s.pop(); } else { cout<<x<<" "; x++; } } } while(s.empty()==0) { cout<<s.top()<<" "; s.pop(); } return 0; }
\(T3\) C. 节日 \(0pts\)
总结
- \(T1\) 误以为每种情况的概率相同,手摸了一整场的样例 \(2\) 也没模出来,破防了。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18722787,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。