zoukankan      html  css  js  c++  java
  • 【Topcoder 12004】SetAndSet 容斥+并查集

    题意

    Topcoder 12004
    把一堆数分成两堆,要求这两堆各自的按位与和相等,求分的方案数。
    ((n le 50, 0 le A_i < 2^ {20}))

    题解

    注意到这个 (A_i) 很小, (n) 也很小,看起来适合状压、容斥。
    考虑容斥各二进制位,如果一个位置被钦定不合法,那么这位上所有的(0)都属于同一个联通块(一个位置合法,当且仅当所有数该位都是1或者两堆各自至少有一个0)
    并查集维护,最后统计。
    不开long long见祖宗!

    代码

    code
    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define mp make_pair
    typedef long long ll;
    const int N = 55, lim = 21;
    class SetAndSet{
    	public: 
    	int n, fa[lim][N], cnt[lim], id[lim][N]; ll ans;
    	int find(int pos, int x){
    		return fa[pos][x] = ((x == fa[pos][x]) ? x : find(pos, fa[pos][x]));
    	}
    	bool merge(int pos, int x, int y){
    		int fx = find(pos, x), fy = find(pos, y);
    		if(fx == fy) return 0;
    		fa[pos][fx] = fy; return 1;
    	}
    	void dfs(int pos, int num, int op){
    		if(pos == lim)
    			return ans += op * ((1ll << num) - 2), void();
    		if(pos == 0){
    			for(int i = 0; i < n; i++) fa[pos][i] = i;
    		} else {
    			for(int i = 0; i < n; i++) fa[pos][i] = fa[pos - 1][i];
    		}
    		dfs(pos + 1, num, op);
    		if(cnt[pos]){
    			for(int i = 2; i <= cnt[pos]; i++)
    				if(merge(pos, id[pos][i], id[pos][1])) num--;
    			dfs(pos + 1, num, -op); 
    		} 
    	}
    	ll countandset(vector<int> A){
    		n = A.size(); ans = 0;
    		for(int i = 0; i < lim; i++){
    			cnt[i] = 0;
    			for(int j = 0; j < n; j++)
    				if(!((A[j] >> i) & 1))
    					id[i][++cnt[i]] = j;
    		} 
    		dfs(0, n, 1);
    		return ans;
    	}
    };
    /*
    O(2^20*nlogn)
    十年OI一场空,没开longlong见祖宗
    */
    
    qaqaq
  • 相关阅读:
    正则表达式分类
    数据思维二三事
    关于编程语言的一些趣史
    重构后端模板文件的一种实践
    为什么程序员需要知道互联网行业发展史
    探秘JS的异步单线程
    Nerd的畅销产品
    Nerd的套现ATM机
    网络传输与加密 (2)
    网络传输与加密
  • 原文地址:https://www.cnblogs.com/zdsrs060330/p/14940167.html
Copyright © 2011-2022 走看看