Codeforces Round #348 (VK Cup 2016 Round 2) E F (2-sat. 待补)

3 篇文章 0 订阅

Little Artem and 2-SAT

题意给两个包含n个变量形式如下的2-sat  f和g

求一种取值使两个2-sat的结果不同


法一:2-sat建图缩点,若两个都无解或只有一个有解就很好办,讨论两个都有解的情况,枚举f中的变量x,若x和!x在一条路径上(如:x -> ... -> !x) 而在g中不是,则可取x=1(或!x = 1),导致f无解,而g不影响。若不存在这样的变量,则枚举x和y, 经过同样的处理即可得出答案。


法二:枚举f中的一个x or y,尝试x=0 && y=0,看g能不能有解,而有解的情况就是g的dag中x和y能到达的点中不同时存在i和!i 


法一代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;


#define eps 1e-9
#define LL long long 
#define ULL unsigned long long
#define N (2000 + 10)
#define M (3000000 + 10)
#define inf 0x3f3f3f3f

int n, m1, m2;

int get(int x) {
	if(x < 0) return n-x;
	return x;
}

struct graph{ 
	int fst[N], vv[M], nxt[M], e;
	int S[N], tot, low[N], pre[N], dc, sccno[N], scnt;
	vector<int> g;
	bitset<N> son[N];
	void init() {
		memset(fst, -1, sizeof fst);
		e = 0;
	}

	void add(int u, int v) {
		vv[e] = v, nxt[e] = fst[u], fst[u] = e++;
	}

	void dfs(int u) {
		pre[u] = low[u] = ++dc;
		S[tot++] = u;
		for(int i = fst[u]; ~i; i = nxt[i]) {
			int v = vv[i];
			if(!pre[v]) {
				dfs(v);
				low[u] = min(low[u], low[v]);
			}
			else if(!sccno[v]) {
				low[u] = min(low[u], pre[v]);
			}
		}
		if(low[u] == pre[u]) {
			++scnt;
			while(tot) {
				int x = S[--tot];
				sccno[x] = scnt;
				if(x == u) break;
			}
		}
	}

	void find_scc() {
		tot = dc = scnt = 0;
		memset(pre, 0, sizeof pre);
		memset(sccno, 0, sizeof sccno);
		for(int i = 1; i <= n*2; ++i) 
			if(!pre[i]) dfs(i);
	}

	void scan(int m) {
		int u, v;
		init();
		for(int i = 0; i < m; ++i) {
			scanf("%d%d", &u, &v);
			add(get(-u), get(v));
			add(get(-v), get(u));
		}
		find_scc();
	}

	bool judge() {
		for(int i = 1; i <= n; ++i) {
			if(sccno[i] == sccno[i+n]) return 0;
		}
		return 1;
	}

	void output() {
		for(int i = 1; i <= n; ++i) {
			printf("%d%c", sccno[i] < sccno[i+n] ? 1 : 0, i == n ? '\n': ' ' );
		}
	}

	void getbit() {
		for(int u = 1; u <= 2*n; ++u) {
			for(int i = fst[u]; ~i; i = nxt[i]) {
				int v = vv[i];
				son[sccno[u]][sccno[v]] = 1;
			}
		}
		for(int i = 1; i <= scnt; ++i) {
			son[i][i] = 1;
			for(int j = 1; j <= scnt; ++j) {
				if(son[j][i])
					son[j] |= son[i];
			}
		}
	}

}g[2];

bool gao(int a, int b) {
	int k = 0;
	for(int i = 1; i <= n; ++i) {
		int u = g[k].sccno[i+a], v = g[k].sccno[i+b];
		int uu = g[k^1].sccno[i+a], vv = g[k^1].sccno[i+b];
		if(g[k].son[u][v] != g[k^1].son[uu][vv]) {
			if(g[k].son[u][v]) k ^= 1;
			g[k].add(i+b, i+a);
			g[k].find_scc();
			g[k].output();
			return 1;
		}
	}
	return 0;
}

int main() {
	scanf("%d%d%d", &n, &m1, &m2);
	g[0].scan(m1);
	g[1].scan(m2);
	bool f1 = g[0].judge(), f2 = g[1].judge();
	if(!f1 && !f2) {
		puts("SIMILAR") ;
	}
	else if(!f1 || !f2) {
		int k = 0;
		if(f2) k^=1;
		g[k].output();
	}
	else {
		g[0].getbit();
		g[1].getbit();
		if(gao(n, 0)) return 0;
		if(gao(0, n)) return 0;
		for(int i = 1; i <= n*2; ++i) {
			for(int j = 1; j <= n*2; ++j) {
				int u = g[0].sccno[i], v = g[0].sccno[j];
				int uu = g[1].sccno[i], vv = g[1].sccno[j];
				if(g[0].son[u][v] != g[1].son[uu][vv]) {
					int k = 0;
					if(g[0].son[u][v]) k = 1;
		
					g[k].add(i > n ? i-n : i + n, i);
					g[k].add(j, j > n ? j - n : j + n);
					g[k].find_scc();
					g[k].output();
					return 0;
				}
			}

		}
		puts("SIMILAR") ;
	}

}



Little Artem and Graph

题意:一开始1到k组成一个完全图,然后添加一个点i连接前面的k个点j(j < i ), 问生成树个数  (n <= 100000, k <= 5);

 

转叉姐的题解:

设 N(v)N(v) 表示点 vv 的邻居,考虑 N(v)N(v) 中编号最大的点 uu,那么 N(v) \subset (N(u) \cup \{u\})N(v)(N(u){u})
把 vv 作为 uu 的儿子,得到一颗(有根)树。我们在树上进行 DP。

DP 的状态是 f(v, S)f(v,S),表示当决策完 vv 这颗子树的点后,N(v)N(v) 里面点的连通性状态是 SS 的方案数。
为了计算 f(v, S)f(v,S),我们转而计算 g(v, i, S)g(v,i,S) 表示决策完 vv 的前 ii 个儿子后,N(v) \cup \{v\}N(v){v} 里面点的连通性状态是 SS 的方案数。
考虑 g(v, 0, S)g(v,0,S),唯一的方案就是考虑 vv 和 N(v)N(v) 之间的边,一共有 2^{|N(v)|}2N(v) 种可能。当 i > 0i>0 时,其实就是枚举一个 g(v, i - 1, S')g(v,i1,S) 和一个 f(c_i, S'')f(ci,S) 进行合并。最后得到了 g(v, \mathrm{deg}(v), S)g(v,deg(v),S) 后,把 SS 里面的点 vv 去掉,就可以得到 f(v, S)f(v,S) 了。


再无耻的贴一个跑的最快的代码,好像就是生成树计数



#include <bits/stdc++.h>
using namespace std;
#define M 11111
typedef long long ll;
const ll mod = 1000000007;

ll n, m;
map<ll, ll> mp[M];
vector<ll> v[M];

ll POW(ll a, ll n) {
    ll s = 1;
    while (n) {
        if (n & 1) s = 1ll * s * a % mod;
        n >>= 1;
        a = 1ll * a * a % mod;
    }
    return s;
}

int main() {
    scanf("%d %d", &n, &m);
    if (m == 1) {
        puts("1");
        return 0;
    }
    for (ll i = 1; i <= m; i++) for (ll j = 1; j <= m; j++) {
        if (i != j) {
            mp[i][j] = mp[j][i] = mod - 1;
        } else {
            mp[i][i] = m - 1;
        }
        if (i > j) {
            v[i].push_back(j);
        }
    }
    for (ll i = m + 1; i <= n; i++) {
        mp[i][i] = m;
        for (ll x, j = 1; j <= m; j++) {
            scanf("%I64d", &x);
            mp[i][x] = mod - 1;
            mp[x][i] = mod - 1;
            mp[x][x] ++;
            v[i].push_back(x);
        }
    }

    ll rlt = 1;
    for (ll i = n - 1; i >= 1; i--) {
        ll c = mp[i][i];
        rlt = 1ll * rlt * c % mod;
        if (!c) {
            puts("0");
            return 0;
        }
        c = POW(c, mod - 2);
        if (i == 1) { break; }
        for (ll j, r = 0; r < v[i].size(); r++) {
            j = v[i][r];
            mp[i][j] = 1ll * c * mp[i][j] % mod;
        }
        for (ll j, r = 0; r < v[i].size(); r++) {
            j = v[i][r];
            ll t = mp[j][i];
            for (ll k, s = 0; s < v[i].size(); s++) {
                k = v[i][s];
                ll w = mp[j][k];
                w -= 1ll * mp[i][k] * t % mod;
                if (w < 0) w += mod;
                mp[j][k] = w;
            }
        }
    }
    printf("%I64d\n", rlt);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值