P14364 Sol || 别样的容斥大战
CarroT1212 · · 题解
一个很丝滑的容斥做法!
设
对于第
以下的“人”和“走”都是针对这
:::info[
因为要计算的方案只有全部走掉这一种,那么对于所有符合情况的方案,一定有
反过来,如果在这样的
换句话说,因为走人情况是固定的,所以符合情况的排列方案的
在这里就是计算所有
由于
受上述做法启发,我们尝试去分别计算每个走人情况被多少排列方案取到。这样
枚举走人情况,容易根据这个情况求出此时的
具体来说,对于一个
出现了另一个方向的限制,导致我们并不好像
- 对于这种两个相反方向的限制,我们可以通过对其中一个方向容斥,使得这些方向的限制,一部分变得反向,另一部分没有限制。那就变成了很多个同向限制问题,可能会更好处理。类似思想的题目有 AGC036F P5405 等。
这题也是同理的。我们容斥有一部分
删去容斥之后没有限制的位,现在相当于有
:::success[
for (ll s=0;s<1ll<<k;s++) if (__builtin_popcount(s)>=m) {
ll res=0;
for (ll t=s;;t=(t-1)&s) {
ll rw=1,x=0,y=0;
for (ll i=1;i<=k;i++) {
if (s>>i-1&1^1) (rw*=max(sum[w[i]+x]-x-y,0ll))%=P,x++;
else if (t>>i-1&1) (rw*=max(sum[w[i]+x]-x-y,0ll))%=P,y++;
}
(res+=(y&1?P-1:1)*rw%P*fac[n-x-y])%=P;
if (!t) break;
}
(ans+=res)%=P;
}
:::
可以发现这些容斥贡献都可以摊到 DP 过程里计数。设
:::success[正解代码]
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std; bool MEM;
using ll=long long; using ld=long double;
using pii=pair<int,int>; using pll=pair<ll,ll>;
const int I=1e9;
const ll J=1e18,N=507,P=998244353;
ll fac[N];
ll n,m,c[N],sum[N],k,w[N],ans; int dp[N][N][N];
char s[N];
void mian() {
fac[0]=1; for (ll i=1;i<N;i++) fac[i]=fac[i-1]*i%P;
scanf("%lld%lld%s",&n,&m,s+1);
for (ll i=1;i<=n;i++) if (s[i]-'0'==1) k++,w[k]=i-k;
if (!k) return cout<<"0",void();
for (ll i=1;i<=n;i++) scanf("%lld",&c[i]),sum[c[i]]++;
for (ll i=1;i<=n;i++) sum[i]+=sum[i-1];
dp[0][0][0]=1;
for (ll i=1,o=1;i<=k;i++) for (ll x=0;x<i;x++) for (ll y=0;x+y<i;y++) {
(dp[i][x+1][y]+=dp[i-1][x][y]*max(sum[w[i]+x]-x-y,0ll)%P)%=P; // 指定第 i 个人走
(dp[i][x][y+1]+=dp[i-1][x][y]*max(sum[w[i]+x]-x-y,0ll)%P)%=P; // 指定他没走,容斥它
(dp[i][x][y]+=dp[i-1][x][y])%=P; // 指定他没走,不容斥它
}
for (ll x=0;x<=k-m;x++) for (ll y=0;x+y<=k;y++)
(ans+=(y&1?P-1:1)*dp[k][x][y]%P*fac[n-x-y])%=P;
cout<<ans;
}
bool ORY; int main() {
// while (1)
// int t; for (scanf("%d",&t);t--;)
mian();
cerr<<"\n"<<abs(&MEM-&ORY)/1048576<<"MB";
return 0;
}
:::
:::warning[我赛时干啥了] 贡献延迟计算做法没试出来怎么转移,很悲伤。
然后我对着 A 性质的错误转化想了总共至少半小时,才发现转化是错的,并得到了一个没有可推广性的 A 性质做法。
感觉时间不够做出这题了,尝试再拼几个好分,接着想
这时我才发现我完全没尝试过直接计数一个走人情况的方案数。然后我很快意识到了可以容斥。
但是我脑子一昏想错了一个细节:我的
这是不应该的。因为我们原本研究出的
也就是,你尝试容斥的不应该是“至少有哪些人走了”(就算
这个时候时间实在太晚了,我还没反应过来,不敢再修了,拼好分去了。 :::