• COCI2012 TOY


    m种物品,n个箱子之中装着若干物品。问取出一些箱子后,所有m种物品都被选出的方案数。

    m<=20,n<=106

    这道题很妙啊 深刻地利用了容斥

    看到n=20,我们就想到了状压和容斥。

    怎么容斥呢?我们设A(s)表示状态为s的集合的数量,那么A(2^m-1)就是所求答案。但是这个不好做,那么我们用容斥放缩一下这个条件。我们设B(s)表示状态为s的子集的集合的数量,那么我们可以得到以111为例 B(111)=A(111)+B(110)+B(101)+B(011)-B(100)-B(010)-B(001)+B(000) 那么化简一下 A(111)=B(111)-B(110)-B(101)-B(011)+B(100)+B(010)+B(001)-B(000) 标准的容斥形式。那么剩下的问题就是求出B的值。

    我们定义B(s)是s的子集的集合的数量,那么我们只要枚举s的子集就行了,统计数量只要在读入时统计一下就行了。但是枚举子集的复杂度是3^m,过不去,那么我们再优化。

    这里我们用了一种分治的方法来优化复杂度,我们利用一种类似cdq分治的办法,把复杂度优化到m*2^m。具体见代码。

    然后就是容斥了。

    这里证明一下子集枚举的复杂度:我们不妨设二元对(s,t)。那么我们不妨用三进制来表达关系,0s是t的子集,1s和t没关系,2t是s的子集。那么这就是3^m了。

    还有一种可以省去popcount的枚举子集的方法,就是用递归,但是程序中没有实现,想知道的可以留个言教导一下蒟蒻。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 100010, mod = 1000000007;
    int n, all, m;
    ll ans; 
    int c[N], d[N];
    inline ll power(ll x, ll t)
    {
        ll ret = 1;
        for(; t; t >>= 1, x = x * x % mod) if(t & 1) ret = ret * x % mod;
        return ret;
    }
    void build(int l, int r)
    {
        if(l > r) return;
        if(l == r) { c[l] = d[l]; return; }
        int mid = (l + r) >> 1;
        build(l, mid); build(mid + 1, r);
        for(int i = l; i <= mid; ++i) c[i + mid - l + 1] = (c[i + mid - l + 1] + c[i]) % mod; 
    }
    int main()
    {
        scanf("%d%d", &n, &m); all = 1 << m;
        for(int i = 1; i <= n; ++i)
        {
            int num, tot = 0; scanf("%d", &num);
            while(num--)
            {
                int x; scanf("%d", &x); --x;
                tot |= (1 << x);
            }
            ++d[tot]; 
        }
        build(0, all - 1);
        for(int i = 0; i < all; ++i) 
        {
            int x = __builtin_popcount(i);
            if((x % 2) == (m % 2)) ans = (ans + power(2, c[i])) % mod; else ans = ((ans - power(2, c[i])) % mod + mod) % mod;
        }
        printf("%lld
    ", ans);
        return 0;
    }
  • 相关阅读:
    sql语句
    数据结构
    Collection接口
    【学习笔记】〖九度OJ〗题目1443:Tr A
    【学习笔记】〖九度OJ〗题目1104:整除问题
    【学习笔记】〖九度OJ〗题目1138:进制转换
    【学习笔记】〖九度OJ〗题目1326:Waiting in Line
    【学习笔记】〖九度OJ〗题目1437:To Fill or Not to Fill
    【学习笔记】〖九度OJ〗题目1153:括号匹配问题
    【学习笔记】〖九度OJ〗题目1161:Repeater
  • 原文地址:https://www.cnblogs.com/19992147orz/p/6686388.html
Copyright © 2020-2023  润新知