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

来源:互联网 发布:专情的网络语句 编辑:程序博客网 时间:2024/04/29 02:26

E 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 0x3f3f3f3fint 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") ;}}



F 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 11111typedef 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;}


0 0
原创粉丝点击