zoukankan      html  css  js  c++  java
  • G. Mercenaries (组合数,容斥,数学)

    题目:传送门

    题意

    有 n 个区间,你可以选 i 这个区间的条件是,你选的所有区间的总数介于 [ li, ri ] 之间,有 m 对限制条件,每个限制条件输入两个数 u, v,表示区间 u 和 v 不能同时被选上。问你有多少种不同的满足条件的取法。输出答案对 998244353 取模, 1 <= n <= 3e5; 0 <= m <= min(n * (n - 1) / 2,  20);

    思路

    若没有限制条件,那么我们可以用 cnt[i] 表示取 i 个区间,可以被选的区间数。那么取 i 个区间的方案数就是 C(cnt[i], i);

    对于每个区间 [l, r],当取的点数在区间 [l, r] 内时,这个区间可以被选上,所以,cnt[ l ]++, cnt[ r + 1] --,最后,让 cnt[i] 累加起来,就得到真正的 cnt[i] 了。

    现在,考虑加入了限制条件,由于最多只有 20 对限制条件,可以想到用状压暴力枚举所有状态。

    我们可以先算出所有不符合题意的情况,最后用全集减去不符的就是最终的答案。

    那么我们可以暴力枚举所有情况,当二进制的第 i 位为 1 时,表示选上第 i 个区间的两个数,然后,去重,可以得到现在已经选上的数的个数。

    详情看代码注释。

    #include <bits/stdc++.h>
    #define LL long long
    #define ULL unsigned long long
    #define UI unsigned int
    #define mem(i, j) memset(i, j, sizeof(i))
    #define rep(i, j, k) for(int i = j; i <= k; i++)
    #define dep(i, j, k) for(int i = k; i >= j; i--)
    #define pb push_back
    #define make make_pair
    #define INF 0x3f3f3f3f
    #define inf LLONG_MAX
    #define PI acos(-1)
    #define fir first
    #define sec second
    #define lb(x) ((x) & (-(x)))
    #define dbg(x) cout<<#x<<" = "<<x<<endl;
    using namespace std;
    
    const int N = 1e6 + 5;
    
    const LL mod = 998244353;
    
    int n, m;
    
    struct note {
    
        int l, r;
    
    }a[N];
    
    int u[N], v[N], cnt[N];
    
    LL sum[50][N];
    
    LL fac[N], ifac[N];
    
    LL ksm(LL a, LL b) {
    
        LL res = 1LL;
    
        while(b) {
    
            if(b & 1) res = res * a % mod;
    
            a = a * a % mod; b >>= 1;
    
        }
    
        return res;
    
    }
    
    void init() { /// 预处理阶乘
    
        fac[0] = 1LL;  ifac[0] = 1LL;
    
        rep(i, 1, 300000) fac[i] = 1LL * i * fac[i - 1] % mod;
    
        ifac[300000] = ksm(fac[300000], mod - 2);
    
        dep(i, 0, 299999) ifac[i] = 1LL * (i + 1) * ifac[i + 1] % mod;
    
    }
    
    LL C(int n, int m) { /// 组合数
    
        if(n < m || m < 0) return 0;
    
        return fac[n] * ifac[m] % mod * ifac[n - m] % mod;
    
    }
    
    set < int > Q;
    
    void solve() {
    
        scanf("%d %d", &n, &m);
    
        rep(i, 1, n) {
    
            scanf("%d %d", &a[i].l, &a[i].r);
    
            cnt[a[i].l]++;
    
            cnt[a[i].r + 1]--;
    
        }
    
        rep(i, 1, n) cnt[i] += cnt[i - 1]; /// 累加起来
    
        rep(i, 0, m - 1) {
    
            scanf("%d %d", &u[i], &v[i]);
    
        }
        
        
        /// 预处理, sum[i][j] 表示选了 j 个区间,其中有 i 个是限制条件上的区间的方案书。
        rep(i, 0, 2 * m) {
    
            rep(j, 1, n) {
    
                sum[i][j] = (sum[i][j - 1] + C(cnt[j] - i, j - i)) % mod;
    
            }
    
        }
    
        LL ans = 0LL;
    
        rep(i, 0, (1 << m) - 1) { /// 暴力枚举
    
            int l = 1, r = n;
    
            bool flag = 0; ///容斥,选了0个限制条件 - 选了1个限制条件 + 选了 2 个限制条件 ... ,也就是 i 的二进制位有偶数个1就加上,否则就减去
    
            Q.clear(); /// set自动去重
    
            rep(j, 0, m - 1) {
    
                if((i >> j) & 1) {
    
                    Q.insert(u[j]);
    
                    Q.insert(v[j]);
                    
                    /// 求限制条件区间的交集
                    
                    l = max(l, a[u[j]].l);
    
                    l = max(l, a[v[j]].l);
    
                    r = min(r, a[u[j]].r);
    
                    r = min(r, a[v[j]].r);
    
                    flag = !flag;
    
                }
    
            }
    
            int up = Q.size(); /// 有 up 个区间是在限制条件上的
    
            LL tmp = 0;
    
            if(l <= r) tmp = (sum[up][r] - sum[up][l - 1] + mod) % mod; ///取的总区间的个数介于 交集 l ~ r 之间,才能保证当前状态所有有限制条件的区间都能被选上
    
            if(!flag) ans = (ans + tmp) % mod;
    
            else ans = (ans - tmp + mod) % mod;
    
        }
    
        printf("%lld
    ", ans);
    
    }
    
    
    int main() {
    
        init();
    
    //    int _; scanf("%d", &_);
    //    while(_--) solve();
    
        solve();
    
        return 0;
    }
  • 相关阅读:
    Linux下高并发socket最大连接数所受的各种限制
    Oracle DB 使用资源管理
    Oracle DB 资源管理
    C++ 封装私有堆(Private Heap)
    用宏实现 C++ Singleton 模式
    基于 crt debug 实现的 Windows 程序内存泄漏检测工具
    如何养成良好的 C++ 编程习惯 —— 内存管理
    OCP-1Z0-053-V12.02-643题
    Oracle DB 通过SQL 优化管理性能
    OCP-1Z0-052-V8.02-141题
  • 原文地址:https://www.cnblogs.com/Willems/p/13643123.html
Copyright © 2011-2022 走看看