您的位置:首页 > 其它

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

法一代码

#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(c​i​​,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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: