• 省选测试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;
    }
    
  • 相关阅读:
    Javascript创建对象的几种方式【转】
    log4net轻松使用日期作为动态文件名【转】
    使用jquery的lazy loader插件实现图片的延迟加载
    Oracle通用分页存储过程的创建与使用
    Oracle 11g R2的卸载与重装
    Oracle的rownum的原理和使用【转】
    Remoting客户端和服务端两种方式的调用总结
    动态执行SQL语句
    Trie模板
    Dijkstrapriority_queue
  • 原文地址:https://www.cnblogs.com/shawk/p/14379856.html
Copyright © 2020-2023  润新知