NOIP后模拟赛总结

字符串测试1

A 回文子串

  • 正解是求出以i为结尾的回文串个数,修改的时候前k个暴力算,后面都是k,r后k个暴力算,询问的时候前k个暴力算,线段树维护一下即可
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define H(l, r) (h[r] - h[l-1] * p[r-(l)+1])
#define F(l, r) (f[l] - f[r+1] * p[r-(l)+1])

typedef unsigned long long ull;
const int N = 5e4 + 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;
}

char s[N];
bool tag[N*4];
ull h[N], f[N], p[N];
int n, k, m, t[N*4], a[N];

void Build(int rt, int l, int r) {
    if (l == r) {
        for (int i = l - k + 1; i <= l; ++i)
            if (H(i, l) == F(i, l)) t[rt]++;
        return;
    }
    int mid = l + r >> 1;
    Build(ls, l, mid); Build(rs, mid+1, r);
    t[rt] = t[ls] + t[rs];
}

void Pushdown(int rt, int l, int r) {
    int mid = l + r >> 1; tag[rt] = 0;
    t[ls] = k * (mid - l + 1);
    t[rs] = k * (r - mid);
    tag[ls] = tag[rs] = 1;
}

void Change(int rt, int l, int r, int x, int y) {
    if (x <= l && r <= y) return t[rt] = k * (r - l + 1), tag[rt] = 1, void();
    if (tag[rt]) Pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (x <= mid) Change(ls, l, mid, x, y);
    if (y > mid) Change(rs, mid+1, r, x, y);
    t[rt] = t[ls] + t[rs];
}

void Modify(int rt, int l, int r, int x, int w) {
    if (l == r) return t[rt] = w, void();
    if (tag[rt]) Pushdown(rt, l, r);
    int mid = l + r >> 1;
    if (x <= mid) Modify(ls, l, mid, x, w);
    else Modify(rs, mid+1, r, x, w);
    t[rt] = t[ls] + t[rs];
}

int Ask(int rt, int l, int r, int x, int y) {
    if (x <= l && r <= y) return t[rt];
    if (tag[rt]) Pushdown(rt, l, r);
    int mid = l + r >> 1, ans = 0;
    if (x <= mid) ans = Ask(ls, l, mid, x, y);
    if (y > mid) ans += Ask(rs, mid+1, r, x, y);
    return ans;
}

void Init(int l, int r) {
    if (l < 1) l = 1;
    for (int i = l; i <= r; ++i)
        h[i] = h[i-1] * 131 + s[i];
    for (int i = r; i >= l; --i)
        f[i] = f[i+1] * 131 + s[i];
}

int Cal(int l, int r, int g = 0, int ans = 0) {
    if (!g && l < k) l = k;
    if (g && l < 1) l = 1;
    if (r > n) r = n;
    if (g == 1) {
        g = 1;
    }
    Init(l - k + 1, r);
    for (int j = l; j <= r; ++j) {
        int s = 0;
        for (int i = j; i >= j - k + 1 && i >= 1 && (!g || i >= l); --i) if (H(i, j) == F(i, j)) s++;
        if (g) ans += s;
        else Modify(1, k, n, j, s);
    }
    return ans;
}

int main() {
    scanf("%s", s + 1); 
    n = strlen(s + 1); 
    k = read(); m = read();
    p[0] = 1;
    for (int i = 1; i <= n; ++i)
        p[i] = p[i-1] * 131;
    Init(1, n);
    Build(1, k, n);
    while (m--) {
        int od = read(), l = read(), r = read();
        if (od == 1) {
            char c; scanf("%c", &c);
            memset(s + l, c, sizeof(char) * (r - l + 1));
            if (r - l + 1 >= k) Change(1, k, n, l + k - 1, r);
            Cal(l, std::min(l + k - 2, r)); Cal(r + 1, r + k - 1);
        }
        else printf("%d
", Cal(l, std::min(l + k - 2, r), 1) + (r - l + 1 >= k ? Ask(1, k, n, l + k - 1, r) : 0));
    }
    return 0;
}

B Ernd

  • 建出后缀自动机,然后在后缀自动机上DP

  • 走Trie树上的边是在结尾加个字母,从t[x].f走到x会加若干的字母,这需要注意

Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>

const int N = 4e5 + 5;

struct Edge {
    int n, t;
}e[N];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

struct Tree {
    int f, s[26];
}t[N];

int n, len[N], sz[N], trc, rt, in[N], q[N], l, r;
long long f[N];
char s[N];

void Dfs(int x) {
    for (int i = h[x], y; i; i = e[i].n)
        Dfs(y = e[i].t), sz[x] += sz[y];
}

int main() {
    scanf("%d%s", &n, s + 1);
    trc = rt = 1;
    for (int i = 1; i <= n; ++i) {
        int x = rt, c = s[i] - 'a', y;
        len[rt = ++trc] = len[x] + 1; sz[rt] = 1;
        for (; x && !t[x].s[c]; x = t[x].f) t[x].s[c] = rt;
        if (!x) t[rt].f = 1;
        else if (len[y = t[x].s[c]] == len[x] + 1) t[rt].f = y;
        else {
            t[++trc] = t[y]; len[trc] = len[x] + 1;
            t[rt].f = t[y].f = trc;
            for (; x && t[x].s[c] == y; x = t[x].f) t[x].s[c] = trc;
        }
    }
    for (int i = 2; i <= trc; ++i)
        Add(t[i].f, i);
    Dfs(1); sz[1] = 0;
    for (int x = 1; x <= trc; ++x) {
        for (int i = h[x]; i; i = e[i].n)
            in[e[i].t]++;
        for (int i = 0; i < 26; ++i)
            in[t[x].s[i]]++;
    }
    q[l = r = 1] = 1;
    while (l <= r) {
        int x = q[l++];
        for (int i = h[x]; i; i = e[i].n) {
            int y = e[i].t;
            f[y] = std::max(f[y], f[x] + 1ll * sz[y] * (len[y] - len[x]));
            if (!--in[y]) q[++r] = y;
        }
        for (int i = 0; i < 26; ++i) {
            int y = t[x].s[i];
            f[y] = std::max(f[y], f[x] + sz[y]);
            if (!--in[y]) q[++r] = y;
        }
    }
    printf("%lld
", f[rt]);
    return 0;
}

C 字符串 (Unaccepted)

  • 最后n2暴力
Show Code

D

Show Code



数据结构测试2

A 森林

  • 树上主席树,启发式合并,然后没了
Show Code
#include <cstdio>
#include <algorithm>
#define l(x) t[x].l
#define r(x) t[x].r

const int N = 8e4 + 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*2];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

struct Tree {
    int l, r, s;
}t[N*300];

int trc, rt[N], n, m, q, ans, tp[N], a[N], b[N];
int dep[N], fa[17][N], sz[N];

void Add(int &rt, int l, int r, int x) {
    t[++trc] = t[rt]; t[rt=trc].s++;
    if (l == r) return;
    int mid = l + r >> 1;
    if (x <= mid) Add(l(rt), l, mid, x);
    else Add(r(rt), mid + 1, r, x);
}

int Ask(int rt1, int rt2, int rt3, int rt4, int l, int r, int k) {
    if (l == r) return b[l];
    int mid = l + r >> 1, s = t[l(rt1)].s + t[l(rt2)].s - t[l(rt3)].s - t[l(rt4)].s;
    if (s >= k) return Ask(l(rt1), l(rt2), l(rt3), l(rt4), l, mid, k);
    else return Ask(r(rt1), r(rt2), r(rt3), r(rt4), mid + 1, r, k - s);
}

void Dfs(int x, int top) {
    sz[x] = 1; tp[x] = top; 
    dep[x] = dep[fa[0][x]] + 1;
    for (int i = 0; i < 16; ++i)
        fa[i+1][x] = fa[i][fa[i][x]];
    Add(rt[x] = rt[fa[0][x]], 1, m, a[x]);
    for (int i = h[x], y; i; i = e[i].n)
        if ((y = e[i].t) != fa[0][x])
            fa[0][y] = x, Dfs(y, top), sz[x] += sz[y];
}

int Lca(int x, int y) {
    if (dep[x] < dep[y]) std::swap(x, y);
    for (int k = dep[x] - dep[y], i = 0; k; k >>= 1, ++i)
        if (k & 1) x = fa[i][x];
    if (x == y) return x;
    for (int i = 16; i >= 0; --i)
        if (fa[i][x] != fa[i][y])
            x = fa[i][x], y = fa[i][y];
    return fa[0][x];
}

int main() {
    read(); n = read(); m = read(); q = read();
    for (int i = 1; i <= n; ++i)
        b[i] = a[i] = read();
    while (m--) {
        int x = read(), y = read();
        Add(x, y); Add(y, x);
    }
    std::sort(b + 1, b + n + 1);
    m = std::unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; ++i)
        a[i] = std::lower_bound(b + 1, b + m + 1, a[i]) - b;
    for (int i = 1; i <= n; ++i)
        if (!tp[i]) Dfs(i, i);
    while (q--) {
        char od; scanf(" %c", &od);
        int x = read() ^ ans, y = read() ^ ans;
        if (od == 'L') {
            if (tp[x] == tp[y]) continue;
            if (sz[tp[x]] < sz[tp[y]]) std::swap(x, y);
            Add(x, y); Add(y, x);
            fa[0][y] = x; sz[tp[x]] += sz[tp[y]];
            Dfs(y, tp[x]);
        }
        else {
            int lca = Lca(x, y);
            printf("%d
", ans = Ask(rt[x], rt[y], rt[lca], rt[fa[0][lca]], 1, m, read() ^ ans));
        }
    }
    return 0;
}

B 最大异或和

  • 可持久化01Trie树,最后20分钟才想起来题目中的式子可以转换为全部的异或和异或上前缀异或和,最后忘了特判l和r都为1的情况了
Show Code
#include <cstdio>

const int N = 6e5 + 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;
}

int n, m, a[N], t[N*25], c[N*25][2], trc, rt[N], s[N];

void Insert(int &rt, int x, int k = 23) {
    c[++trc][0] = c[rt][0]; c[trc][1] = c[rt][1]; 
    t[trc] = t[rt] + 1; rt = trc; 
    if (k >= 0) Insert(c[rt][(x >> k) & 1], x, k - 1);
}

int Ask(int l, int r, int x, int k = 23) {
    if (k < 0) return 0;
    bool y = ((x >> k) & 1) ^ 1;
    if (t[c[r][y]] - t[c[l][y]]) return Ask(c[l][y], c[r][y], x, k - 1) + (1 << k);
    else return Ask(c[l][y^1], c[r][y^1], x, k - 1);
}

int main() {    
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) {
        a[i] = a[i-1] ^ read();
        Insert(rt[i] = rt[i-1], a[i]);
    }
    while (m--) {
        char od; scanf(" %c", &od);
        if (od == 'A') ++n, Insert(rt[n] = rt[n-1], a[n] = a[n-1] ^ read());
        else {
            int l = read(), r = read();
            if (l == r && l == 1) {
                printf("%d
", a[n] ^ read());
                continue;
            }
            if (l == 1) l++;
            printf("%d
", Ask(rt[l-2], rt[r-1], read() ^ a[n]));
        }
    }
    return 0;
}

C Generating Synergy

  • Kdtree,打懒惰标记就好了
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ls t[rt].l
#define rs t[rt].r

const int N = 1e5 + 5, M = 1e9 + 7;

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];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

struct Node {
    int x, y;
}a[N], q1, q2;

struct Tree {
    int l, r, tag, c;
    Node p, s, b;
}t[N];

int n, m, dfn[N], dfc, dep[N], sz[N], k, trc, ans;

void Dfs(int x, int fa) {
    sz[x] = 1;
    dfn[x] = ++dfc;
    dep[x] = dep[fa] + 1;
    a[x] = (Node) {dfn[x], dep[x]};
    for (int i = h[x], y; i; i = e[i].n)
        Dfs(y = e[i].t, x), sz[x] += sz[y];
}

bool Cmp(const Node &a, const Node &b) {
    return k ? a.x < b.x : a.y < b.y;
}

bool operator <= (const Node &a, const Node &b) {
    return a.x <= b.x && a.y <= b.y;
}

bool operator > (const Node &a, const Node &b) {
    return a.x > b.x || a.y > b.y;
}

bool operator < (const Node &a, const Node &b) {
    return a.x < b.x || a.y < b.y;
}

void Pushup(int rt) {
    t[rt].s = t[rt].b = t[rt].p;
    if (ls)
        t[rt].s.x = std::min(t[rt].s.x, t[ls].s.x), t[rt].s.y = std::min(t[rt].s.y, t[ls].s.y),
        t[rt].b.x = std::max(t[rt].b.x, t[ls].b.x), t[rt].b.y = std::max(t[rt].b.y, t[ls].b.y);
    if (rs)
        t[rt].s.x = std::min(t[rt].s.x, t[rs].s.x), t[rt].s.y = std::min(t[rt].s.y, t[rs].s.y),
        t[rt].b.x = std::max(t[rt].b.x, t[rs].b.x), t[rt].b.y = std::max(t[rt].b.y, t[rs].b.y);
}

int Build(int l, int r, int nk) {
    if (l > r) return 0;
    int mid = l + r >> 1, rt = ++trc; k = nk;
    std::nth_element(a + l, a + mid, a + r + 1, Cmp);
    t[rt].p = a[mid]; t[rt].c = 1; nk ^= 1;
    t[rt].l = Build(l, mid - 1, nk);
    t[rt].r = Build(mid + 1, r, nk);
    Pushup(rt); return rt;
}

void Change(int rt, int c) {
    if (!rt || t[rt].b < q1 || q2 < t[rt].s) return;
    if (q1 <= t[rt].s && t[rt].b <= q2) return t[rt].c = t[rt].tag = c, void();
    if (t[rt].tag) t[ls].c = t[ls].tag = t[rs].c = t[rs].tag = t[rt].tag, t[rt].tag = 0; //Pushdown
    if (q1 <= t[rt].p && t[rt].p <= q2) t[rt].c = c;
    Change(ls, c); Change(rs, c);
}

int Ask(int rt) {
    if (!rt || q1 > t[rt].b || t[rt].s > q1) return 0;
    if (q1 <= t[rt].p && t[rt].p <= q1) return t[rt].c;
    if (t[rt].tag) t[ls].c = t[ls].tag = t[rs].c = t[rs].tag = t[rt].tag, t[rt].tag = 0; //Pushdown
    int ans = Ask(ls);
    if (!ans) ans = Ask(rs);
    return ans;
}

int main() {
    int T = read();
    while (T--) {
        memset(h, 0, n * 4 + 4); 
        memset(t, 0, sizeof(Tree) * (trc + 1));
        edc = ans = trc = 0;
        n = read(); read(); m = read();
        for (int i = 2; i <= n; ++i)
            Add(read(), i);
        Dfs(1, 0); Build(1, n, 1);
        for (int i = 1; i <= m; ++i) {
            int x = read(), l = read(), c = read();
            q1 = (Node) {dfn[x], dep[x]};
            q2 = (Node) {dfn[x] + sz[x] - 1, dep[x] + l};
            if (c) Change(1, c);
            else if ((ans += 1ll * i * Ask(1) % M) >= M) ans -= M;
        }
        printf("%d
", ans);
    }
}

D Alo

  • 正解是枚举次大值位置,显然求出极大的满足次大值就是枚举的这个位置的区间会包括所有的符合条件的区间

  • 显然会有从左边第2个比它大的数的后一个位置到右边边第1个比它大的数和从左边第1个比它大的数到右边边第2个比它大的数的前一个位置,

  • 因为找的是区间最大值,所以这个极大的区间就是从左边第2个比它大的数的后一个位置到右边边第2个比它大的数的前一个位置

  • 问题就是找左边或右边比一个数大的第2个数,容易发现单调栈是不可行的

  • 维护一个链表,从小到大删去链表中的数,删去一个数时链表里一定都是比这个数大的,然后向前向后跳2个就求出来了

  • 找到这个区间后可持久化01Trie就可以解决了

  • 不过这题数据水,暴力找两个端点其实就可以了,实测碾压标程10倍

Show Code
#include <cstdio>
#include <algorithm>

const int N = 5e4 + 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;
}

int n, a[N], ans;

int main() {
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i <= n; ++i) {
        int cnt = 0;
        for (int j = i - 1; j >= 1; --j) {
            if (a[j] > a[i] && ++cnt > 1) break;
            ans = std::max(ans, a[i] ^ a[j]);
        }
        cnt = 0;
        for (int j = i + 1; j <= n; ++j) {
            if (a[j] > a[i] && ++cnt > 1) break;
            ans = std::max(ans, a[i] ^ a[j]);
        }
    }
    printf("%d
", ans);
    return 0;
}



数据结构测试1

A 弹跳 (Unaccepted)

  • kdtree
Show Code

B 魔法森林 (Unaccepted)

  • lct
Show Code

C 小凸玩矩阵

  • 二分答案二分图最大匹配检验是否可以选出n-k+1个小于等于二分的答案的数
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>

const int N = 505;

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, d;
}e[N*N>>1];
int h[N], edc, tmp[N];

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y, 1}; h[x] = edc;
    e[++edc] = (Edge) {h[y], x, 0}; h[y] = edc;
}

int n, m, k, s, t, dep[N], q[N], l, r, a[N>>1][N>>1];

bool Bfs() {
    memcpy(h, tmp, t * 4 + 4);
    memset(dep, 0, t * 4 + 4);
    dep[s] = 1; q[l=r=1] = s;
    while (l <= r) {
        int x = q[l++];
        for (int i = h[x]; i; i = e[i].n) {
            int y = e[i].t;
            if (!e[i].d || dep[y]) continue;
            dep[y] = dep[x] + 1; q[++r] = y;
            if (y == t) return 1;
        }
    }
    return 0;
}

int Dinic(int x, int lim) {
    if (x == t) return lim;
    int sum = 0;
    for (int i = h[x]; i && lim; i = e[i].n) {
        int y = e[i].t; h[x] = i;
        if (!e[i].d || dep[y] != dep[x] + 1) continue;
        int f = Dinic(y, std::min(lim, e[i].d));
        sum += f; lim -= f;
        e[i].d -= f; e[i^1].d += f;
    }
    if (!sum) dep[x] = 1;
    return sum;
}

bool Judge(int mid) {
    memset(h, 0, t * 4 + 4); edc = 1;
    for (int i = 1; i <= n; ++i)
        Add(s, i);
    for (int j = 1; j <= m; ++j)
        Add(j + n, t);
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            if (a[i][j] <= mid) Add(i, j + n);
    int ans = 0;
    memcpy(tmp, h, t * 4 + 4);
    while (Bfs()) ans += Dinic(s, n);
    return ans >= k;
}

int main() {
    freopen("matrix.in", "r", stdin);
    freopen("matrix.out", "w", stdout);
    n = read(); m = read(); k = n - read() + 1;
    int l = 1e9, r = 0; s = n + m + 1; t = s + 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            a[i][j] = read();
            if (l > a[i][j]) l = a[i][j];
            if (r < a[i][j]) r = a[i][j];
        }
    }
    while (l < r) {
        int mid = l + r >> 1;
        if (Judge(mid)) r = mid;
        else l = mid + 1;
    }
    printf("%d
", l);
    return 0;
}



模拟赛x+1

A 接力比赛

  • 看着就像背包,开始也没想复杂度,然后就直接跑了背包,讲题的时候才发现是个n3的DP
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>

typedef long long ll;
const int N = 1e6 + 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;
}

int n, m, s1, s2;
ll f1[N], f2[N], ans;

int main() {
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    n = read(), m = read();
    memset(f1, 0xcf, sizeof(f1));
    memset(f2, 0xcf, sizeof(f2));
    f1[0] = f2[0] = 0;
    for (int i = 1; i <= n; ++i) {
        int v = read(), w = read(); s1 += v;
        for (int j = s1; j >= v; --j)
            f1[j] = std::max(f1[j], f1[j-v] + w);
    }
    for (int i = 1; i <= m; ++i) {
        int v = read(), w = read(); s2 += v;
        for (int j = s2; j >= v; --j)
            f2[j] = std::max(f2[j], f2[j-v] + w);
    }
    for (int i = 1; i <= s1 && i <= s2; ++i)
        if (ans < f1[i] + f2[i]) ans = f1[i] + f2[i];
    printf("%lld
", ans);
    return 0;
}

B 树上竞技

  • 考虑每条边的贡献,一定是特殊点少的那一边会经过这条边,假设一边有i个点,另一边就有n-i个点(假定i<n-i),则有

[sum_{i=1}^{m-1} inom{s}{i} inom{n-s}{m-i} min{i,m-i}=2 imes sum_{i=1}^{lfloor frac{m-1}{2} floor} i inom{s}{i} inom{n-s}{m-i} + [2|m] imes inom{s}{frac{m}{2}} inom{n-s}{frac{m}{2}} frac{m}{2} ]

  • 后面的 那部分可以暴力计算,主要是前半部分

[sum_{i=1}^{lfloor frac{m-1}{2} floor} i inom{s}{i} inom{n-s}{m-i}=s imes sum_{i=1}^{lfloor frac{m-1}{2} floor} inom{s-1}{i-1} inom{n-s}{m-i} ]

  • 然后设(G(i)=sum_{i=1}^{lfloor frac{m-1}{2} floor} inom{s-1}{i-1} inom{n-s}{m-i}),计算这个时间复杂度也不对

  • 然后考虑这个式子的组合意义:有一个长为 n-1 的序列,一共选 m-1 个数,而在前 s-1 个数中至多选 k-1 个数,剩下的数在后面选,考虑如何推到 G(s+1),其计数在前 s 个数中至多选 k-1,只与 G(s) 不同如果在前 s-1 个数中选了 k-1 个数,位置 s 就不能选了,所以减去在前面 s-1 个数中强制选 k-1 个数,强制选位置 s,在后面剩下的 n-s 个数中随便选 m-k-1 个数的方案数,所以

[G(s+1)=G(s)-inom{s-1}{k-1} inom{n-s}{m-k-1} ]

  • 最后就是当k为0的时候G的组合意义是无意义的,所以G都是0
Show Code
#include <cstdio>

const int N = 1e6 + 5, M = 1e9 + 7;

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];
int h[N], edc;

void Add(int x, int y) {
    e[++edc] = (Edge) {h[x], y}; h[x] = edc;
}

int n, m, k, f[N], siz[N], ans;
int fac[N], fai[N];

int Pow(int a, int k, int ans = 1) {
    for (; k; k >>= 1, a = 1ll * a * a % M)
        if (k & 1) ans = 1ll * ans * a % M;
    return ans;
}

int C(int n, int m) {
    if (n < 0 || m < 0 || n < m) return 0;
    return 1ll * fac[n] * fai[m] % M * fai[n-m] % M;
}

void Dfs(int x, int fa) {
    siz[x] = 1;
    for (int i = h[x], y; i; i = e[i].n) {
        if ((y = e[i].t) == fa) continue;
        Dfs(y, x); siz[x] += siz[y];
    }
}

int main() {
    freopen("meeting.in", "r", stdin);
    freopen("meeting.out", "w", stdout);
    n = read(), m = read(); k = m - 1 >> 1;
    for (int i = 2; i <= n; ++i) 
        Add(read(), i);
    Dfs(1, 0);
    fac[0] = 1;
    for (int i = 1; i <= n; ++i)
        fac[i] = 1ll * fac[i-1] * i % M;
    fai[n] = Pow(fac[n], M - 2);
    for (int i = n; i >= 1; --i)
        fai[i-1] = 1ll * fai[i] * i % M;
    if (k > 0) f[1] = C(n - 1, m - 1);
    for (int i = 1; i < n; ++i)
        if ((f[i+1] = f[i] - 1ll * C(i - 1, k - 1) * C(n - 1 - i, m - 1 - k) % M) < 0) f[i+1] += M;
    for (int i = 1; i <= n; ++i)
        f[i] = 1ll * f[i] * i % M;
    for (int i = 2, s = siz[i]; i <= n; s = siz[++i])
        if ((ans += (f[s] + f[n-s] + (m & 1 ? 0ll : 1ll) * C(s, m / 2) * C(n - s,  m / 2) % M * m / 2) % M) >= M) ans -= M;
    printf("%d
", ans);
    return 0;
}

C 虚构推理

  • 正解是二分答案,然后计算所有指针的可行范围的交集

  • 还可以暴力水过,枚举时间logn计算,精度确实不太够

Show Code
#include <cstdio>
#include <algorithm>

const int N = 5e4 + 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;
}

int n;
double a[N], b[N], ans = 1e9;

double Dis(double a, double b) {
    if ((a -= b) < 0) a = -a;
    return a > 180 ? 360 - a : a;
}

double Cal(double x, double *a) {
    int i = std::lower_bound(a + 1, a + n + 1, x > 180 ? x - 180 : x + 180) - a, j = i - 1;
    return std::max(Dis(x, a[i > n ? 1 : i]), Dis(x, a[j < 1 ? n : j]));
}

int main() {
    freopen("unreal.in", "r", stdin);
    freopen("unreal.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read() % 12 * 30 + (b[i] = read() * 6 + read() * 0.1) / 12;
    std::sort(a + 1, a + n + 1);
    std::sort(b + 1, b + n + 1);
    for (int i = 0; i < 360; i += 30)
        for (double m = 0; m < 360; m += 0.01)//这里精度不够的话还可以是0.00015,在小就T了
            ans = std::min(ans, std::max(Cal(i + m / 12, a), Cal(m, b)));
    printf("%.9lf
", ans);
    return 0;
}

D 记忆碎片 (Unaccepted)

Show Code



原文地址:https://www.cnblogs.com/shawk/p/14147676.html