Codeforces Round #348 (VK Cup 2016 Round 2) E F (2-sat. 待补)
2016-05-13 15:26
441 查看
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
法一代码
F Little Artem and Graph
题意:一开始1到k组成一个完全图,然后添加一个点i连接前面的k个点j(j < i ), 问生成树个数 (n <= 100000, k <= 5);
转叉姐的题解:
设
把
DP。
DP 的状态是
为了计算
考虑
再无耻的贴一个跑的最快的代码,好像就是生成树计数
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 , vv[M], nxt[M], e; int S , tot, low , pre , dc, sccno , scnt; vector<int> g; bitset<N> son ; 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)|}2∣N(v)∣种可能。当
i > 0i>0时,其实就是枚举一个
g(v, i - 1, S')g(v,i−1,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; }
相关文章推荐
- opencv图像写入视频详解
- HDU 3932 Groundhog Build Home (模拟退火算法)
- jQuery使用ajaxSubmit()提交表单以及AjaxSubmit的一些用法
- spring MVC权限分配的实现
- 什么是类?什么是对象?类和对象有什么关系?
- 记计账典型用户和用户场景
- centos中分析java占用大量CPU资源的原因
- iOS 图层和动画(1)
- Python实现正则表达式匹配任意的邮箱
- redis-c api
- java程序优化
- POJ,2420 A Star not a Tree?(模拟退火算法)
- 布隆过滤器-----时间+空间
- 搁置已久的XD_JWXT的wp
- oracle中having的用法
- Android项目重构之路:架构篇
- CSS 中单位px和em,rem的区别
- 合并有序数组
- xib的简单介绍与使用
- CentOS7 mini的安装和静态ip设置