[CF1525E]Assimilation IV

壹、题目描述 ¶

传送门 to CF.

贰、题解 ¶

显然,这是要单独考虑每个城市对于答案的贡献。

对于每个城市,我们可以考虑它被覆盖的情况有多少种,但是尝试之后不难发现,这样计算太复杂了 —— 一个城市可以只被一个点覆盖,也可能被两个,三个......

考虑容斥?容斥什么?怎么容斥?时间复杂度再怎么也要个 \(2^n\) 吧?复杂度 \(\mathcal O(m2^n)\)?Soshite?

显然,这个思路不可取,但是我们总不可能离开 “方案” 吧?但是我们可以考虑正难则反

对于一个城市,总的方案数有 \(n!\) 种,我们只需要计算出不能覆盖它的点选择情况就好了。

显然,在第 \(i\) 次选择点的时候,选择点的距离必须要严格大于 \(n-i+1\),所以,我们记 \(cnt(i,j)\) 表示到达城市 \(i\) 的点中,距离严格大于 \(j\) 的有多少。

那么对于一个点,它不被覆盖的方案数即

\[\prod_{i=1}^{n}(cnt(i,n-j+1)-j+1) \]

为什么后面还要 \(-j+1\)?因为在前面 \([1,i-1]\) 次选择种,已经选了一些点,而我们不能重复选择这些已经被选择的点,所以得减去。

但是注意特殊情况,当出现 \(cnt(i,n-j+1)-j+1\le 0\) 时,说明这个点无论如何都会被选择,直接让方案数为 \(0\) 即可。

叁、参考代码 ¶

const int maxn=20;
const int maxm=5e4;
const int mod=998244353;

inline int qkpow(int a, int n){
    int ret=1;
    for(; n; n>>=1, a=1ll*a*a%mod)
        if(n&1) ret=1ll*ret*a%mod;
    return ret;
}

int d[maxn+5][maxm+5];
int n, m;
int cnt[maxm+5][maxn+5];

inline void input(){
    n=readin(1), m=readin(1);
    rep(i, 1, n) rep(j, 1, m)
        d[i][j]=readin(1);
}

signed main(){
    input();
    rep(i, 1, m) rep(j, 1, n) rep(k, 1, n) if(d[k][i]>j)
        ++cnt[i][j];
    int ans=0, fac=1;
    rep(i, 1, n) fac=1ll*fac*i%mod;
    rep(i, 1, m){
        int cur=1;
        rep(j, 1, n){
            if(cnt[i][n-j+1]-j+1<=0){ cur=0; break; }
            cur=1ll*cur*(cnt[i][n-j+1]-j+1)%mod;
        }
        ans=(0ll+ans+fac+mod-cur)%mod;
    }
    ans=1ll*ans*qkpow(fac, mod-2)%mod;
    printf("%d\n", ans);
    return 0;
}

肆、用到 の Trick ¶

正着算方案时,如果遇到困难,何不试试正难则反?

posted @ 2021-05-19 21:44  Arextre  阅读(130)  评论(0编辑  收藏  举报