zoukankan      html  css  js  c++  java
  • 【BZOJ】3495: PA2010 Riddle

    题意

    (n(1 le n le 1000000))个城市,(k(1 le k le n))个国家,(m(1 le m le 1000000))条边。要求每个国家有且仅有一个首都,每条边两端的城市至少要有一个首都。判断是否有解。

    分析

    满足性问题。而且每个城市只有两种情况,首都or不是首都。所以考虑2-sat

    题解

    对于每一个点,拆点为(i)(i'),表示有首都和无首都。
    对于每一个国家(假设有(a)个城市),由于只有一个首都,也就是说,假设这个国家的第(j)城市是首都,则前(j-1)个城市和后(a-j)个城市都不是首都!对应着前缀和和后缀和为0!
    所以我们对每个国家建立前缀和和后缀和的结点,由于只有两种情况,0和1,所以照样用2-sat可以解决。
    至于怎么连边,自己yy一下,很简单的。

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1000005*6, M=1000005*12;
    int ihead[N], cnt, FF[N], LL[N], p[N], scc, tot;
    struct E {
    	int next, to;
    }e[M];
    void add(int x, int y) {
    	e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt;
    }
    void dfs(int x) {
    	static int tid=0, s[N], vis[N], top=0;
    	s[++top]=x;
    	vis[x]=1;
    	FF[x]=LL[x]=++tid;
    	for(int i=ihead[x]; i; i=e[i].next) {
    		int y=e[i].to;
    		if(!FF[y]) {
    			dfs(y);
    			LL[x]=min(LL[x], LL[y]);
    		}
    		else if(vis[y]) {
    			LL[x]=min(LL[x], FF[y]);
    		}
    	}
    	if(FF[x]==LL[x]) {
    		++scc;
    		int y;
    		do {
    			y=s[top--];
    			vis[y]=0;
    			p[y]=scc;
    		} while(x!=y);
    	}
    }
    bool check() {
    	for(int i=1, all=tot<<1|1; i<=all; ++i) {
    		if(!FF[i]) {
    			dfs(i);
    		}
    	}
    	for(int i=1; i<=tot; ++i) {
    		if(p[i<<1]==p[i<<1|1]) {
    			return 0;
    		}
    	}
    	return 1;
    }
    int main() {
    	int n, m, k;
    	scanf("%d%d%d", &n, &m, &k);
    	for(int i=1; i<=m; ++i) {
    		int x, y;
    		scanf("%d%d", &x, &y);
    		add(x<<1|1, y<<1);
    		add(y<<1|1, x<<1);
    	}
    	tot=n;
    	for(int j=1; j<=k; ++j) {
    		scanf("%d", &m);
    		for(int i=1; i<=m; ++i) {
    			int a;
    			scanf("%d", &a);
    			int now=tot+i;
    			if(i!=1) {
    				add(now<<1|1, (now-1)<<1|1);
    				add((now-1)<<1, now<<1);
    				add(a<<1, (now-1)<<1|1);
    			}
    			else {
    				add(a<<1|1, now<<1|1);
    			}
    			add(now<<1|1, a<<1|1);
    			add(a<<1, now<<1);
    
    			now=tot+m+i;
    			if(i!=m) {
    				add(now<<1|1, (now+1)<<1|1);
    				add((now+1)<<1, now<<1);
    				add(a<<1, (now+1)<<1|1);
    			}
    			else {
    				add(a<<1|1, now<<1|1);
    			}
    			add(now<<1|1, a<<1|1);
    			add(a<<1, now<<1);
    		}
    		tot+=2*m;
    	}
    	puts(check()?"TAK":"NIE");
    	return 0;
    }
  • 相关阅读:
    linux上安装mysql
    linux 上nginx配置
    js人民币数字转大写
    pm2常用命令
    基于redis实现分布式锁
    Enterprise Architect 14破解版 安装包 安装教程
    一些有用的链接
    Linux安装Zookeeper
    根据朋友圈的网易云音乐分享找到人
    我的待做事项
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4986018.html
Copyright © 2011-2022 走看看