• NOI 模拟赛


    T1 HNOI2015 实验比较

    给 $n$ 个有权值的物品,$m$ 条消息,消息可以是“小于”或者“等于”,一个物品只会与一个小于等于它的东西比较,求最后权值排名方案数 mod 998244353

    $n leq 500$

    sol:

    考场上自闭了,考出来更自闭

    相等的节点缩起来,是一个森林,你要做的是给这个森林编号,要求儿子的编号小于父亲的,求方案数

    然后 dp 就完事了,$f_{(x,i)}$ 表示 $x$ 为根的子树划分成 $i$ 个编号的方案数,合并两个子树实质上是合并两个编号序列搞个 $g_{(i,j,k)}$ 表示把 $i$ 个编号和 $j$ 个编号的序列合成 $k$ 个编号的序列的方案数,一起转移即可

    #include <bits/stdc++.h>
    #define LL long long
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch;
        for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    int n, m;
    const int mod = 1e9 + 7, maxn = 510;
    int fa[maxn], u[maxn], v[maxn], ind[maxn];
    inline int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
    int first[maxn], to[maxn << 1], nx[maxn << 1], cnt;
    inline void add(int u, int v) {if(u == v) return;to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; ind[v]++;}
    int vis[maxn], size[maxn];
    LL d[maxn][maxn][maxn], dp[maxn][maxn];
    void dfs(int x) {
        dp[x][0] = 1; LL tmp[maxn]; memset(tmp, 0, sizeof(tmp));
        for(int i=first[x];i;i=nx[i]) {
            if(to[i] == x) continue;
            dfs(to[i]);
            dwn(j, size[x], 0) rep(k, 1, size[to[i]]) rep(l, max(j, k), (j + k)) (tmp[l] += (1LL * (1LL * dp[x][j] * dp[to[i]][k] % mod) * d[j][k][l] % mod)) %= mod;
            size[x] += size[to[i]];
            rep(j, 0, size[x]) dp[x][j] = tmp[j], tmp[j] = 0;
        }
        size[x]++;
        dwn(i, size[x], 1) dp[x][i] = dp[x][i - 1];
    }
    int find_circle(int x) {
        if(vis[x] == -1) return 1; vis[x] = -1;
        for(int i=first[x];i;i=nx[i])
            if(!vis[to[i]] && find_circle(to[i])) return 1;
            else if(vis[to[i]] == -1) return 1;
        vis[x] = 1;
        return 0;
    }
    int main() {
        //freopen("sort.in","r",stdin);
        //freopen("sort.out","w",stdout);
        n = read(), m = read();
        rep(i, 1, n) fa[i] = i; char ch[2]; 
        rep(i, 1, m) {
            scanf("%d%s%d", &u[i], ch, &v[i]);
            if(ch[0] == '=') fa[find(u[i])] = find(v[i]);
        }
        rep(i, 1, m) add(find(u[i]), find(v[i]));
        rep(i, 1, n) if(!vis[find(i)] && find_circle(find(i))) {cout << 0 << endl; return 0;}
        d[0][0][0] = 1;
        rep(i, 0, n+1) rep(j, 0, n+1) rep(k, max(i, j), (i + j)) {
            if((!i) && (!j)) continue;
            if(i) (d[i][j][k] += d[i - 1][j][k - 1]) %= mod;
            if(j) (d[i][j][k] += d[i][j - 1][k - 1]) %= mod;
            if(i && j) (d[i][j][k] += d[i - 1][j - 1][k - 1]) %= mod;
            //cout << d[i][j][k] << endl;
        }
        //cout << d[2][3][4] << endl;
        rep(i, 1, n) if(!ind[find(i)]) add(n + 1, find(i));
        dfs(n + 1); LL ans = 0;
        rep(i, 1, n+1) (ans += dp[n+1][i]) %= mod;
        cout << ans << endl;
    }
    View Code

    T2 bzoj3681 Arietta

    甚至刚做过

    #include <bits/stdc++.h>
    #define LL long long
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch;
        for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 100010, maxm = 1000010, oo = 2147483233;
    struct Dinic {
        int cur[maxm], head[maxm], nx[maxm];
        int n, m, s, t;
        struct Edge {
            int from, to, caps;
            Edge(){}
            Edge(int _1, int _2, int _3): from(_1), to(_2), caps(_3) {}
        }es[maxm];
        void AddEdge(int u, int v, int w) {
            es[m] = Edge(u, v, w); nx[m] = head[u]; head[u] = m++;
            es[m] = Edge(v, u, 0); nx[m] = head[v]; head[v] = m++;
        }
        void setn(int _) {n = _;}
        Dinic() {m = 0; memset(head, -1, sizeof(head)); clo = 0;}
        queue<int> q; int dis[maxm], vis[maxm], clo;
        bool BFS() {
            //rep(i, 0, n) dis[i] = 0;
            q.push(t); vis[t] = ++clo; dis[t] = 0;
            while(!q.empty()) {
                int now = q.front(); q.pop(); cur[now] = head[now];
                for(int i=head[now];~i;i=nx[i]) {
                    Edge &e = es[i^1];
                    if((vis[e.from] != clo) && e.caps) {
                        vis[e.from] = clo;
                        dis[e.from] = dis[now] + 1;
                        q.push(e.from);
                    }
                }
            }
            return (vis[s] == clo);
        }
        int DFS(int u, int a) {
            if(u == t || !a) return a;
            int flow = 0, f;
            for(int &i=cur[u];~i;i=nx[i]) {
                Edge &e = es[i];
                if(dis[e.to] == dis[u] - 1 && (f = DFS(e.to, min(e.caps, a)))) {
                    e.caps -= f; es[i^1].caps += f;
                    a -= f; flow += f;
                    if(!a) return flow;
                }
            }
            return flow;
        }
        int MaxFlow(int _s, int _t) {
            s = _s, t = _t; int flow = 0;
            while(BFS()) {
                flow += DFS(s, 2147483233);
            }
            return flow;
        }
    } sol;
    int s, t, nodes;
    int n, m;
    int fa[maxn], v[maxn];
    int first[maxn], nx[maxn], to[maxn], cnt;
    inline void add(int u, int v) { to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt; }
    int root[maxn], ls[maxm << 1], rs[maxm << 1], dfn;
    inline void Insert(int &x, int l, int r, int pos, int p) {
        x = ++dfn;
        if(l == r) {
            sol.AddEdge(p, x + nodes, oo);
            return;
        } int mid = (l + r) >> 1;
        if(pos <= mid) Insert(ls[x], l, mid, pos, p) ;
        else Insert(rs[x], mid + 1, r, pos, p) ;
        if(ls[x]) sol.AddEdge(ls[x] + nodes, x + nodes, oo);
        if(rs[x]) sol.AddEdge(rs[x] + nodes, x + nodes, oo);
    }
    inline int merge(int x, int y, int l, int r) {
        if(!x || !y) return x + y;
        int z = ++dfn;
        if(l == r) {
            sol.AddEdge(x + nodes, z + nodes, oo);
            sol.AddEdge(y + nodes, z + nodes, oo);
            return z;
        } int mid = (l + r) >> 1;
        ls[z] = merge(ls[x], ls[y], l, mid);
        rs[z] = merge(rs[x], rs[y], mid+1, r);
        if(ls[z]) sol.AddEdge(ls[z] + nodes, z + nodes, oo);
        if(rs[z]) sol.AddEdge(rs[z] + nodes, z + nodes, oo);
        return z;
    }
    inline void AddEDG(int x, int l, int r, int L, int R, int p) {
        if(!x) return;
        if(L <= l && r <= R) {
            sol.AddEdge(x + nodes, p, oo);
            return;
        } int mid = (l + r) >> 1;
        if(L <= mid) AddEDG(ls[x], l, mid, L, R, p);
        if(R > mid) AddEDG(rs[x], mid+1, r, L, R, p);
    }
    inline void dfs(int x) {
        Insert(root[x], 1, n, v[x], x);
        for(int i=first[x];i;i=nx[i]) {
            dfs(to[i]);
            root[x] = merge(root[x], root[to[i]], 1, n);
        }
    }
    int main() {
        n = read(), m = read(); s = n + m + 1, t = n + m + 2, nodes = t + 1;
        rep(i, 2, n) {
            fa[i] = read();
            add(fa[i], i);
        } rep(i, 1, n) {
            v[i] = read();
            sol.AddEdge(s, i, 1);
        } dfs(1); rep(i, 1, m) {
            int l = read(), r = read(), k = read(), ct = read();
            AddEDG(root[k], 1, n, l, r, i+n);
            sol.AddEdge(i + n, t, ct);
        } sol.setn(nodes + dfn + 5);
        cout << sol.MaxFlow(s, t) << endl;
    }
    View Code

    T3 bzoj3772 精神污染

    给一棵树,给 $m$ 条路径,随机选两条编号不同的,求一条包含另一条的概率

    $n,m leq 100000$

    sol:

    考场上想的是开个 vector 记录每个左端点的右端点都是啥,然后对欧拉序建主席树,每次把当前点挂的所有右端点差分成入栈 +1,出栈 -1

    然后枚举每一条路径,定位到每条路径对应的线段树,分别查 $x->lca$,$y->lca$,$lca->lca$ 的和最后 $-1$ 就可以了(因为欧拉序好像不太滋磁直接查一条不是祖孙链的路径)

    这个东西常数奇大,空间还卡,满数据 1.2s 而且没啥可优化的了

    后来发现数两个矩形里的点就可以了

    不过调 4K 的代码还是挺爽的

    一下午写了 10K,去世

    #include <bits/stdc++.h>
    #define LL long long
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch;
        for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 530000;
    int n, m, u[maxn], v[maxn];
    int first[maxn], to[maxn << 1], nx[maxn << 1], cnt;
    inline void add(int u, int v) {
        to[++cnt] = v;
        nx[cnt] = first[u];
        first[u] = cnt;
    }
    int ind[maxn], oud[maxn], dep[maxn], anc[maxn][21], dfn;
    void dfs(int x) {
        ind[x] = ++dfn; rep(i, 1, 20) anc[x][i] = anc[anc[x][i - 1]][i - 1];
        for(int i=first[x];i;i=nx[i]) {
            if(to[i] == anc[x][0]) continue;
            anc[to[i]][0] = x;
            dep[to[i]] = dep[x] + 1;
            dfs(to[i]);
        }
        oud[x] = dfn;
    }
    inline int get_son(int x, int y) {
        int t = dep[x] - dep[y] - 1;
        rep(i, 0, 20) if(t & (1 << i)) x = anc[x][i];
        return x;
    }
    int cntp, cntq;
    struct Point {
        int x, y;
        Point(){};
        Point(int u, int v) {x = u, y = v;}
        bool operator < (const Point &b) const {
            return x == b.x ? y < b.y : x < b.x;
        }
        bool operator == (const Point &b) const { return x == b.x && y == b.y; }
    }ps[maxn];
    struct Ques {
        int x, l, r, opt;
        Ques(){}
        Ques(int _x, int _l, int _r, int _opt) {x = _x, l = _l, r = _r, opt = _opt;}
        bool operator < (const Ques &b) const { return x < b.x; }
    }qs[maxn];
    void AddPoint(int x, int y) {
        if(x < y) swap(x, y);
        ps[++cntp] = Point(x, y);
    }
    void AddArea(int x, int y, int u, int v) {
        if(u > v || x > y) return;
        if(x < u) swap(x, u), swap(y, v);
        qs[++cntq] = Ques(x, u, v, 1);
        qs[++cntq] = Ques(y + 1, u, v, -1);
    }
    int c[maxn];
    inline int lowbit(int x) {return x & (-x);}
    inline void update(int x, int opt) {for(;x <= n; x += lowbit(x)) c[x] += opt;}
    inline int cal(int x) {int res = 0; for(; x; x -= lowbit(x)) res += c[x]; return res;}
    int main() {
        //freopen("path.in","r",stdin);
        //freopen("path.out","w",stdout);
        n = read(), m = read();
        rep(i, 2, n) {
            int u = read(), v = read();
            add(u, v); add(v, u);
        } dfs(1);
        rep(i, 1, m) {
            int u = read(), v = read();
            AddPoint(ind[u], ind[v]);
            if(dep[u] < dep[v]) swap(u, v);
            if(u == v) AddArea(ind[u], oud[u], 1, ind[u]), AddArea(ind[u], oud[u], oud[u] + 1, n);
            else if(ind[v] <= ind[u] && ind[u] <= oud[v]) {
                int cur = get_son(u, v);
                //cout << cur << endl;
                AddArea(ind[u], oud[u], 1, ind[cur] - 1);
                AddArea(ind[u], oud[u], oud[cur] + 1, n);
            }
            else AddArea(ind[u], oud[u], ind[v], oud[v]);
        } sort(ps + 1, ps + cntp + 1); sort(qs + 1, qs + cntq + 1);
        LL ans = 0; int cur = 1;
        rep(i, 1, cntp) {
            while(cur <= cntq && qs[cur].x <= ps[i].x) update(qs[cur].l, qs[cur].opt), update(qs[cur].r + 1, -qs[cur].opt), cur++;
            ans += cal(ps[i].y);
        }
        int jj;
        for(int i=1;i<=m;i=jj) {
            for(jj = i; ps[jj] == ps[i]; jj++);
            ans -= (jj - i) * (jj - i - 1) / 2;
        } //cout << ans << endl;
        ans -= m;
        LL ans2 = 1LL * m * (m - 1) / 2; LL g = __gcd(ans, ans2);
        ans /= g; ans2 /= g;
        long double fans = 1.00 * ans / ans2;
        double pans = fans; printf("%.6f
    ", pans);
    }
    View Code

    upd:T2 T 成暴力分,T3 MLE

    不过学习了网络流的优秀写法

    T3 主席树常数太大过不了,需要扫描线树状数组

    下面是考场写的 TLE + MLE 代码 qnq ↓

    #include <bits/stdc++.h>
    #define LL long long
    #define rep(i, s, t) for (register int i = (s), i##end = (t); i <= i##end; ++i)
    #define dwn(i, s, t) for (register int i = (s), i##end = (t); i >= i##end; --i)
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch;
        for (ch = getchar(); !isdigit(ch); ch = getchar()) if (ch == '-') f = -f;
        for (; isdigit(ch); ch = getchar()) x = 10 * x + ch - '0';
        return x * f;
    }
    const int maxn = 550050;
    int n, m;
    int first[maxn], to[maxn << 1], nx[maxn << 1], cnt;
    inline void add(int u, int v) {to[++cnt] = v; nx[cnt] = first[u]; first[u] = cnt;}
    struct Ques {
        int l, r;
        bool operator < (const Ques &b) const {
            return (l == b.l) ? (r < b.r) : (l < b.l);
        }
        bool operator == (const Ques &b) const {
            return (l == b.l) && (r == b.r);
        }
    }qs[maxn];
     
    struct Chaintable {
        int head[maxn], nx[maxn], val[maxn], cnt;
        Chaintable() {cnt = 0;}
        void AddItem(int u, int v) {val[++cnt] = v; nx[cnt] = head[u]; head[u] = cnt;}
    }ct;
     
    //vector<int> G[maxn];
    int fa[maxn], dep[maxn], size[maxn], bl[maxn], ind[maxn], oud[maxn], mxs[maxn], dfn;
    void dfs1(int x) {
        size[x] = 1; ind[x] = ++dfn;
        for(int i=first[x];i;i=nx[i]) {
            if(to[i] == fa[x]) continue;
            fa[to[i]] = x; dep[to[i]] = dep[x] + 1;
            dfs1(to[i]); size[x] += size[to[i]];
            if(!mxs[x] || size[to[i]] > size[mxs[x]]) mxs[x] = to[i];
        } oud[x] = ++dfn;
    }
    void dfs2(int x, int col) {
        bl[x] = col;
        if(!mxs[x]) return;
        dfs2(mxs[x], col);
        for(int i=first[x];i;i=nx[i])
            if(to[i] != fa[x] && to[i] != mxs[x]) dfs2(to[i], to[i]);
    }
    int lca(int x, int y) {
        while(bl[x] != bl[y]) {
            if(dep[bl[x]] < dep[bl[y]]) swap(x, y);
            x = fa[bl[x]];
        }
        return dep[x] < dep[y] ? x : y;
    }
    int root[maxn], ls[maxn * 45], rs[maxn * 45], val[maxn * 45], ToT;
    void build(int &x, int l, int r) {
        x = ++ToT;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(ls[x], l, mid); build(rs[x], mid + 1, r);
    }
    void Insert(int &x, int pre, int l, int r, int pos, int opt) {
        x = ++ToT; val[x] = val[pre] + opt;
        if(l == r) return;
        int mid = (l + r) >> 1;
        ls[x] = ls[pre], rs[x] = rs[pre];
        if(pos <= mid) Insert(ls[x], ls[pre], l, mid, pos, opt);
        else Insert(rs[x], rs[pre], mid+1, r, pos, opt);
    }
    int query(int x, int y, int lca, int flca, int l, int r, int L, int R) {
        if(L <= l && r <= R) return val[x] + val[y] - val[lca] - val[flca];
        int mid = (l + r) >> 1, ans = 0;
        if(L <= mid) ans += query(ls[x], ls[y], ls[lca], ls[flca], l, mid, L, R);
        if(R > mid) ans += query(rs[x], rs[y], rs[lca], rs[flca], mid+1, r, L, R);
        return ans;
    }
    void dfs(int x) {
        root[x] = root[fa[x]];
        for(int i=ct.head[x];i;i=ct.nx[i]) {
            int px = ct.val[i];
            Insert(root[x], root[x], 1, dfn, ind[px], 1);
            Insert(root[x], root[x], 1, dfn, oud[px], -1);
        }
        for(int i=first[x];i;i=nx[i]) {
            if(to[i] == fa[x]) continue;
            dfs(to[i]);
        }
    }
    int main() {
        //freopen("path.in","r",stdin);
        //freopen("path.out","w",stdout);
        n = read(), m = read();
        rep(i, 2, n) {
            int u = read(), v = read();
            add(u, v); add(v, u);
        } dfs1(1); dfs2(1, 1);
        //build(root[0], 1, dfn);
        rep(i, 1, m) {
            int u = read(), v = read();
            if(u > v) swap(u, v);
            //G[u].push_back(v);
            ct.AddItem(u, v);
            qs[i].l = u, qs[i].r = v;
        } dfs(1); LL ans1 = 0, ans2 = 0;
        sort(qs + 1, qs + m + 1);
        rep(i, 1, m) {
            int qx = qs[i].l, qy = qs[i].r, anc = lca(qx, qy);
            ans1 += query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[qx]);
            ans1 += query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[qy]);
            ans1 -= query(root[qx], root[qy], root[anc], root[fa[anc]], 1, dfn, ind[anc], ind[anc]);
            ans1 --;
        }
        ans2 = 1LL *m * (m - 1) / 2; int cur;
        for(int i=1;i<=m;i=cur) {
            for(cur = i; qs[cur] == qs[i]; cur++);
            ans1 -= 1LL * (cur - i) * (cur - i - 1) / 2;
        }/*
        rep(i, 1, m) {
            cout << qs[i].l << " " << qs[i].r << endl;
        }*/
        LL GDC = __gcd(ans1, ans2); ans1 /= GDC, ans2 /= GDC;
        double ans = 1.00 * ans1 / ans2;
        printf("%.6f
    ", ans);
    }
    T3
  • 相关阅读:
    vue 集成 vis-network 实现网络拓扑图
    三维空间旋转和Three.JS中的实现
    es6常用新属性(详细整理版)
    css的top和left属性不起作用
    网易云音乐歌单生成外链播放器
    Webstorm常用快捷键备忘
    CPU
    中标麒麟neokylin信息查看
    split分割(拆分)文件
    centos7 安装wps
  • 原文地址:https://www.cnblogs.com/Kong-Ruo/p/10581343.html
Copyright © 2020-2023  润新知