[BZOJ]3495 Riddle 2-Sat 前缀和优化

3495: PA2010 Riddle

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 279   Solved: 97
[ Submit][ Status][ Discuss]

Description

k个国家,几个城市,m条边。
要求每个国家有且仅有一个首都,每条边两端的城市至少要有一个首都。
判断是否有解, 有解输出“TAK”,无解输出"NIE"
1  < =  k, N ,M , < =1000000。

Input

Output

Sample Input

6 5 2
1 2
3 1
1 4
5 2
6 2
3 3 4 2
3 1 6 5

Sample Output

TAK

HINT

Source

[ Submit][ Status][ Discuss]


HOME Back

  暴力建边n^2~岂不gg. 考虑2-Sat的一种常见优化建边方式前缀和优化(网上很多人写了前缀和以及后缀和, 但是推一下发现只用前缀和啊...). 对于某个国家的第i个城市, 如果i为首都那么就可以推出i及i之前的这个国家的城市只有一个首都是正确的, 同时推出i+1及i+1之前的这个国家的城市只有一个首都是正确的, 同时还能推出i-1及i-1之前的这个国家的城市一个首都都没有. 那么建三条边即可(反向同理). 只用判对错的2-Sat简直好写好调.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e6;
int n, m, num, cnt, top, idx, tot, k, all;
int low[maxn], dfn[maxn], s[maxn], h[maxn], scc[maxn];
inline const int read() {
	register int x = 0;
	register char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
	return x;
}
struct edge{ int nxt, v;}e[maxn << 1];
inline void add(const int &u, const int &v) {
    e[++ num].v = v, e[num].nxt = h[u], h[u] = num;
    e[++ num].v = u ^ 1, e[num].nxt = h[v ^ 1], h[v ^ 1] = num;
}
void dfs(int u) {
    s[++ top] = u;
    low[u] = dfn[u] = ++ idx;
    for (int i = h[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (!dfn[v]) 
            dfs(v), low[u] = min(low[u], low[v]);
        else if (!scc[v]) low[u] = min(low[u], dfn[v]);
    }
    if (low[u] == dfn[u]) {
        ++ cnt;
        while (s[top + 1] != u) 
            scc[s[top --]] = cnt;
    }
}
int main() {
	register int i, j, x, y, p, last;
	n = read(), m = read(), k = read();
	for (i = 1; i <= m; ++ i) {
		x = read(), y = read();
		add(x << 1 | 1, y << 1);
	}
	for (i = 1; i <= k; ++ i) {
		p = read();
		for (j = 1; j <= p; ++ j) {
			++ tot;
			x = read();
			if (j ^ 1) {
				add(x << 1, last | 1);
				add(last, (n + tot) << 1);
				add(x << 1, (n + tot) << 1);
			} else add(x << 1, (n + tot) << 1);
			last = (n + tot) << 1;
		}
	}
	all = (n + tot) << 1 | 1;
	for (i = 2; i <= all; ++ i)
		if (!dfn[i]) dfs(i);
	for (i = 1; i <= n; ++ i)
		if (scc[i << 1] == scc[i << 1 | 1]) return puts("NIE"), 0;
	puts("TAK");
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值