zoukankan      html  css  js  c++  java
  • 「POI2011 R1」Conspiracy

    「POI2011 R1」Conspiracy

    解题思路 :

    问题转化为,将点集分成两部分,其中一部分恰好组成一个团,其中另一部分恰好组成一个独立集。

    观察发现,如果求出了一个解,那么答案最多可以在这个解的基础上将一个点从团移到独立集,一个点从独立集移到团。

    证明,如果有两个点从团移到独立集,那么这两个点之间的边就矛盾了,如果有两个点从独立集移到团,那么这两个点之间没有边也矛盾了。

    所以只要我们求出了任意一组解,我们就可以通过枚举哪个点从团内移出去和哪个点从独立集里移进来来求出解的数量。

    再进一步观察,发现这个模型可以用2-sat来建图,把每个点 (x) 拆成 (x)(x') 。表示 (x) 在团里和 (x) 在独立集里,如果 (x,y) 之间有边,那么如果 (x) 选了独立集 (y) 就必须要选团,所以连一条 (x' ightarrow y) 。同理,如果 (x, y) 之间没有边,那么如果 (x) 选了团 (y) 就必须要选独立集,所以连一条 (x ightarrow y') 。最后 2-sat求出一组解即可,总复杂度 (O(n^2))

    code

    /*program by mangoyang*/
    #pragma GCC optimize("Ofast","inline")
    #include<bits/stdc++.h>
    #define inf (0x7f7f7f7f)
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
        int f = 0, ch = 0; x = 0;
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        if(f) x = -x;
    }
    
    const int N = 5005;
    
    bitset<N>mp[N];
    short t[2][N], tot[2], deg[N], n; int ans;
    
    namespace twosat{
    	queue<short> q;
    	bitset<N<<1>mp[N<<1];
    	vector<short> d[N*2], c[N*2], g[N*2];
    	short st[N*2], dfn[N*2], ins[N*2], deg[N*2];
    	short in[N*2], low[N*2], col[N*2], top, id, Index; 
    	inline void spop(int u, int id){
    		ins[u] = 0, col[u] = id, c[id].push_back(u);
    		for(int i = 0; i < g[u].size(); i++) 
    			d[id].push_back(g[u][i]);
    	}
    	inline void tarjan(int u){
    		dfn[u] = low[u] = ++Index, st[++top] = u, ins[u] = 1;
    		for(int i = 0; i < g[u].size(); i++){
    			int v = g[u][i];
    			if(!dfn[v]) tarjan(v), low[u] = min(low[v], low[u]);
    			else if(ins[v]) low[u] = min(dfn[v], low[u]); 
    		}
    		if(dfn[u] == low[u]){
    			++id;
    			while(st[top] != u) spop(st[top--], id);
    			spop(st[top--], id);
    		}
    	}
    	inline void solve(){
    		for(int i = 1; i <= 2 * n; i++) if(!dfn[i]) tarjan(i);
    		for(int i = 1; i <= n; i++) 
    			if(col[i] == col[i+n]){ puts("0"), exit(0); } 
    		for(int i = 1; i <= id; i++)
    			for(int j = 0; j < d[i].size(); j++) 
    				if(col[d[i][j]] != i && !mp[col[d[i][j]]][i]) 
    					mp[col[d[i][j]]][i] = 1, deg[i]++;
    		for(int i = 1; i <= id; i++) if(!deg[i]) q.push(i);
    		for(; !q.empty(); q.pop()){
    			int u = q.front();
    			for(int i = 0; i < c[u].size(); i++){
    				int v = c[u][i];
    				int pos = v <= n ? 0 : 1, v1 = v <= n ? v + n : v - n;
    				if(!in[v1]) 
    					t[pos][++tot[pos]] = v <= n ? v : v - n, in[v] = 1;
    			}
    			for(int i = 1; i <= id; i++)
    				if(mp[u][i] && !--deg[i]) q.push(i);
    		}
    	}
    }
    
    int main(){
    	read(n);
    	for(int i = 1, num, x; i <= n; i++){
    		read(num);
    		for(int j = 1; j <= num; j++) read(x), mp[i][x] = 1;
    	}
    	for(int i = 1; i <= n; i++)
    		for(int j = 1; j <= n; j++) if(i != j){
    			if(mp[i][j]) twosat::g[i+n].push_back(j), deg[i]++;
    			else twosat::g[i].push_back(j+n);
    		}
    	twosat::solve();
    	if(tot[0] && tot[1]) ans++;
    	if(tot[0] > 1) 
    		for(int i = 1; i <= tot[0]; i++)
    			if(deg[t[0][i]] == tot[0] - 1) ans++;
    	for(int i = 1; i <= tot[1]; i++){
    		int flag = 1, cnt = 1, u = 0;
    		for(int j = 1; j <= tot[0]; j++)
    			if(!mp[t[1][i]][t[0][j]]){ 
    				if(++cnt > 1){ flag = 0; break; } 
    				u = t[0][j];
    			}
    		if(flag && !u && tot[1] > 1) ans++;
    		if(flag && u && deg[u] == tot[0]) ans++;
    	}
    	cout << ans;
    	return 0;
    }
    
  • 相关阅读:
    MR案例:内连接代码实现
    分布式缓存DistributedCache
    MR案例:Map-Join
    hadoop随手笔记
    Job流程:决定map个数的因素
    Job流程:提交MR-Job过程
    MR案例:Reduce-Join
    MR案例:倒排索引
    MR案例:路径过滤PathFilter
    MR案例:分区和排序
  • 原文地址:https://www.cnblogs.com/mangoyang/p/10199869.html
Copyright © 2011-2022 走看看