A 编码
题目大意 : 给出n个01串,有的位置不确定,问有没有一种方案能使不存在一个串是另外一个串的前缀
- 一个串确定了一个状态可能会导致另一个串不能成为某个状态,想到用2-sat,可是暴力建复杂度太大,所以用trie树优化一下,用一个前缀
Code
Show Code
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
const int N = 2e6 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Edge {
int n, t;
}e[N*3];
int h[N], edc;
void Add(int x, int y) {
e[++edc] = (Edge) {h[x], y}; h[x] = edc;
x ^= 1, y ^= 1; swap(x, y);
e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}
char s[N];
vector<int> a[N];
int n, t[N][2], trc, tot, dfn[N], low[N], dfc, c[N], stk[N], tp, cnt;
void Insert(int n, int x) {
int rt = 0;
for (int i = 1; i <= n; ++i) {
int p = s[i] - '0';
if (!t[rt][p]) t[rt][p] = ++trc;
rt = t[rt][p];
}
a[rt].push_back(x);
}
void Dfs(int rt, int lt) {
for (int i = 0; i < a[rt].size(); ++i) {
int x = a[rt][i]; tot += 2;
if (lt) Add(lt, tot), Add(lt, x ^ 1);
Add(x, tot); lt = tot;
}
if (t[rt][0]) Dfs(t[rt][0], lt);
if (t[rt][1]) Dfs(t[rt][1], lt);
}
void Tarjan(int x) {
dfn[x] = low[x] = ++dfc; stk[++tp] = x;
for (int i = h[x], y; i; i = e[i].n) {
if (!dfn[y=e[i].t]) Tarjan(y), low[x] = min(low[x], low[y]);
else if (!c[y]) low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x] && ++cnt) while (1) {
c[stk[tp]] = cnt;
if (stk[tp--] == x) return;
}
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%s", s + 1);
int len = strlen(s + 1), p = 0;
for (int j = 1; j <= len; ++j)
if (s[j] == '?') p = j, j = len;
s[p] = '0', Insert(len, i * 2);
s[p] = '1', Insert(len, i * 2 + 1);
}
tot = n * 2 + 1; Dfs(0, 0);
for (int i = 1; i <= tot; ++i)
if (!dfn[i]) Tarjan(i);
for (int i = 2; i <= n + n; i += 2)
if (c[i] == c[i+1]) return puts("NO"), 0;
puts("YES");
return 0;
}
B 哈密顿回路
题目大意 : 给定一个边带权无向完全图,求图中是否存在一条长 L 的从起点出发经过所有点恰好一次并最终回到起点(起点头尾经过两次)的路径。
-
n只有14,折半搜,以任意点位起点,然后把经过的点状态压缩一下,把经过的点和路径长度和最后经过的点放在hash表里
-
此题非常卡常,hash需要注意的是选表头的时候要把多有的状态都考虑到,
Code
Show Code
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N = 20, M = 1e7 + 19;
int n, v;
ll l, f[N][N];
struct Hash {
int n, x, s; ll d;
}h[M];
int head[M], hac;
void Add(int x, int s, ll d) {
int ha = (d + (ull)1e15 * s) % M;
for (int i = head[ha]; i; i = h[i].n) if (h[i].x == x && h[i].s == s && h[i].d == d) return;
h[++hac] = (Hash) {head[ha], x, s, d}; head[ha] = hac;
}
bool Find(int x, int s, ll d) {
int ha = (d + (ull)1e15 * s) % M;
for (int i = head[ha]; i; i = h[i].n)
if (h[i].x == x && h[i].s == s && h[i].d == d) return 1;
return 0;
}
void Dfs1(int x, int t, ll d) {
if (d > l) return;
if (!t) return Add(x, v, d);
v |= 1 << x - 1; t--;
for (int i = 1; i <= n; ++i)
if (!(v & 1 << i - 1))
Dfs1(i, t, d + f[x][i]);
v ^= 1 << x - 1;
}
void Dfs2(int x, int t, ll d) {
if (d > l) return;
if (!t) {
if (Find(x, v ^ 1 ^ (1 << x - 1) ^ ((1 << n) - 1), l - d)) {
puts("possible"); exit(0);
}
return;
}
v |= 1 << x - 1; t--;
for (int i = 1; i <= n; ++i)
if (!(v & 1 << i - 1))
Dfs2(i, t, d + f[x][i]);
v ^= 1 << x - 1;
}
int main() {
scanf("%d%lld", &n, &l);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%lld", &f[i][j]);
Dfs1(1, n >> 1, 0); v = 0;
if (n & 1) Dfs2(1, n - (n >> 1), 0);
else {
for (int i = 1; i <= hac; ++i) {
if (Find(h[i].x, h[i].s ^ 1 ^ (1 << h[i].x - 1) ^ ((1 << n) - 1), l - h[i].d)) {
puts("possible"); exit(0);
}
}
}
puts("impossible");
return 0;
}
C 旅行
题目大意 : 找到一个DFS序使得所有叶子到叶子的路径和的最大值最小
- 很容易想到要二分答案,然后检查的时候类似分治一下就好
Code
Show Code
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar())
x = x * 10 + c - '0';
return x * f;
}
struct Node {
ll a, b;
Node() {}
Node(ll x, ll y) {
if (x > y) swap(x, y);
a = x; b = y;
}
};
bool operator < (const Node &x, const Node &y) {
return x.a < y.a;
}
bool g;
ll l, r, mid;
vector<Node> v[N];
int n, t[N][2], d[N];
void Dfs(int x) {
if (!g) return;
v[x].clear();
int ls = t[x][0], rs = t[x][1], dis = d[x];
if (!ls && !rs) return v[x].push_back((Node) {dis, dis});
Dfs(ls); Dfs(rs);
if (!g) return;
vector<Node> tmp;
for (int i = 0; i < v[ls].size(); ++i) {
ll a = v[ls][i].a, b = v[ls][i].b;
for (int j = 0; j < v[rs].size(); ++j) {
ll c = v[rs][j].a, d = v[rs][j].b;
if (a + c <= mid) tmp.push_back((Node) {b + dis, d + dis});
if (a + d <= mid) tmp.push_back((Node) {b + dis, c + dis});
if (b + c <= mid) tmp.push_back((Node) {a + dis, d + dis});
if (b + d <= mid) tmp.push_back((Node) {a + dis, c + dis});
}
}
if (!tmp.size()) return g = 0, void();
sort(tmp.begin(), tmp.end());
ll d = 1e18;
for (int i = 0; i < tmp.size(); ++i)
if (tmp[i].b < d) d = tmp[i].b, v[x].push_back(tmp[i]);
}
int main() {
n = read();
for (int i = 2; i <= n; ++i) {
int x = read(); d[i] =read();
t[x][t[x][0]!=0] = i; r += d[i];
}
while (l < r) {
mid = l + r >> 1;
if (g = 1, Dfs(1), g) r = mid;
else l = mid + 1;
}
printf("%lld
", l);
return 0;
}