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
原文地址:https://www.cnblogs.com/Kong-Ruo/p/10581343.html