省选测试9

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;
}
原文地址:https://www.cnblogs.com/shawk/p/14379856.html