【题解】UOJ #422. 【集训队作业2018】小Z的礼物

本文探讨了Min-Max容斥原理在解决特定组合数学问题中的应用,通过轮廓线DP技术,将问题转化为求解集合首个元素被选中期望的计算。文章提供了详细的算法思路及代码实现,强调了覆盖方案数的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题解
min-max 容斥 : min-max容斥介绍
或者看我的,虽然非常不详细
转化成求一个集合第一个被选的期望
因为覆盖的方案数只有2 * n * m - n - m
那么就想把覆盖方案数相同的方案放在一起DP
轮廓线DP,f[S][k]表示轮廓线状态为S,覆盖方案为k的方案数。注意这个DP转移的时候要带上min-max容斥的正负号。
知道k以后,期望就是:(2 * n * m - n - m) / k

总结:代码很好写。
一开始忽略了覆盖的方案数只有2 * n * m - n - m
一直没有想出怎么DP
代码很长的原因是前面的模板80行

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-(x)))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ld inf = 2e18;
const int maxn = 100020;
const int mod = 998244353;
const int inv2 = (mod + 1) >> 1;

//NOTES: 任意乘法需要用mul,或者强制用long long。
//注意取模
//====================================basic operation===============================
inline void add(int &x, int y) {
      x += y;
      if (x >= mod) {
          x -= mod;
      } 
}

inline void sub(int &x, int y) {
      x -= y;
      if (x < 0) {
          x += mod;
      }
}

inline int mul(int x, int y) {
      return (int) ((long long) x * y % mod);
}

inline int power(int x, int y) {
    int res = 1;
    while (y) {
    if (y & 1) {
        res = mul(res, x);
    }
    x = mul(x, x);
    y >>= 1;
  }
  return res;
}

inline int inv(int a) {
    int b = mod, u = 0, v = 1;
    while (a) {
        int t = b / a;
        b -= t * a;
        swap(a, b);
        u -= t * v;
        swap(u, v);
    }
    if (u < 0) {
        u += mod;
    }
    return u;
}
//=======================================================================================

int n,m,tot;
char ch[20][120];
int f[2][1 << 6][1300],now,last;

void solve(){
    f[now][0][0] = mod - 1;
    rep(i,1,m){
        rep(j,1,n){
          //  cout<<" cehck: "<<i<<" "<<j<<" \n";
            last = now , now ^= 1;
            memset(f[now],0,sizeof(f[now]));
            rep(k,0,(1 << n) - 1){
                rep(l,0,tot){
                    if ( !f[last][k][l] ) continue;
                    int d = f[last][k][l];
               //     cout<<k<<" "<<l<<" "<<d<<endl;
                    int u = j && ((k >> (j - 2)) & 1) , lc = (k >> (j - 1)) & 1 , nS = k - (lc << (j - 1));
                    //not select
                    add(f[now][nS][l],d);
                    if ( ch[j][i] == '.' ) continue;
                    //select
                    int delta = 4 - (j == 1 || u) - (i == 1 || lc) - (j == n) - (i == m);
                    add(f[now][nS + (1 << (j - 1))][l + delta],mod - d);
                }
            }
        }
    }
  //  cout<<"tot : "<<tot<<endl;
    int ans =0 ;
    rep(k,0,(1 << n) - 1){
        rep(l,1,tot){
            int d = f[now][k][l];
       //     cout<<k<<" "<<l<<" "<<d<<endl;
            add(ans,mul(d,mul(tot,inv(l))));
        }
    }
    cout<<ans<<endl;
}
int main(){
    cin>>n>>m;
    rep(i,1,n) scanf("%s",ch[i] + 1);
    tot = n * m * 2 - n - m;
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值