NOIP第二阶段总结

要不是龙爹放水我就第9了,都好强呀QwQ

noip晚间小测5

阶段排名 8

A 统计

  • 数据太水,最后10分钟改了个n2就过了2e5

  • f[i]为以i为开头的逆序对个数,发现每次排序都会将这些点的f值减去,而且每个只能减去一次,所以找到第一次减是什么时候,树状数组维护即可

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

const int N = 2e5 + 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], f[N], b[N];
long long ans, d[N];

void Add(int x, int w) {
    for (; x; x -= x & -x)
        t[x] = std::min(t[x], w);
}

int Ask(int x, int ans = 1e9) {
    for (; x <= n; x += x & -x)
        ans = std::min(ans, t[x]);
    return ans;
}

int main() {
    freopen("count.in", "r", stdin);
    freopen("count.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = n; i >= 1; --i) {
        for (int x = a[i] - 1; x; x -= x & -x) f[i] += t[x];
        for (int x = a[i]; x <= n; x += x & -x) t[x]++;
        ans += f[i];
    }
    printf("%lld ", ans);
    for (int i = 1; i <= n; ++i) t[i] = m + 1;
    for (int i = 1; i <= m; ++i) {
        int x = read();
        if (!b[x]) b[x] = i;
    }
    for (int i = 1; i <= n; ++i) {
        if (b[i]) Add(a[i], b[i]);
        d[Ask(a[i])] += f[i]; 
    }
    for (int i = 1; i <= m; ++i)
        printf("%lld ", ans -= d[i]);
    return 0;
}

B 点亮 (Unaccepted)

  • 暴力写挂了,成0分了
Show Code



模拟赛40

阶段排名 8

今天这4道题都挺简单,考场上就T2没想出来,可是其他的也没过

A 进化

  • 题意不太好理解,考完被卡了

  • lao星左面出现不了t,所以t开头的就不是lao星的

  • l旁边无论怎么边都会是t,如果有若干的l连起来就不是这两个星的

  • 还有一种我没考虑到,就是没有l的肯定不是这两个星的

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

const int N = 1e7 + 5;
char a[N];

int main() {
    freopen("laotui.in", "r", stdin);
    freopen("laotui.out", "w", stdout);
    int T; scanf("%d", &T);
    while (T--) {
        scanf("%s", a + 1);
        int n = strlen(a + 1), l = 1, t = 1, g = 0;
        if (a[1] == 't') l = 0;
        for (int i = 1; i <= n && (l + t); ++i)
            if ((a[i] != 'l' && a[i] != 't') || ((a[i] == 'l' && (g = 1)) && i + 1 <= n && a[i+1] == 'l')) l = t = 0;
        if (!g) l = t = 0;
        printf("%d %d\n", l, t);
    }
    return 0;
}

B 皇室战争

  • 考场rand了70分,lemon上80分

  • 正解是先n3DP处理出一段区间是否可以全部合并,然后n2Dp一遍求出到i的最大答案

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

typedef long long ll;
const int N = 805;

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, k, a[N];
bool g[N][N];
ll s[N], f[N];

int main() {
    freopen("clash.in", "r", stdin);
    freopen("clash.out", "w", stdout);
    n = read(); k = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read(), s[i] = s[i-1] + read();
    for (int i = n - 1; i >= 1; --i) {
        if (a[i] + a[i+1] <= k) g[i][i+1] = 1;
        for (int j = i + 3; j <= n; j += 2) {
            g[i][j] = ((a[i] + a[j] <= k) && (g[i+1][j-1]));
            for (int l = i + 1; l < j && !g[i][j]; ++l)
                g[i][j] = (g[i][l] && g[l+1][j]);
        }
    }
    for (int i = 1; i <= n; ++i, f[i] = f[i-1])
        for (int j = i - 1; j >= 0; j--)
            f[i] = std::max(f[i], f[j] + (g[j+1][i] ? s[i] - s[j] : 0ll));
    printf("%lld\n", f[n]);
    return 0;
}

C MC

  • 显然时光回溯

  • 考场上写了个假的线段树合并,不过按大小优化了一下

  • 没考虑一个边被删了多次的情况,这样的话应该在第一次删的时候合并,被卡了30分(std也写错了,后来改对之后就把我卡了)

  • 最坑人的是还有智商为0的生物,题目中说星球原来的生物的智商是正整数,新加的时候没说,我就以为没有这种情况,15分没了(我感觉我就是这种生物)

Show Code
#include <cstdio>
#include <algorithm>
#define ls t[rt].l
#define rs t[rt].r
#define lx t[x].l
#define rx t[x].r

const int N = 1e5 + 5, M = 19260817;

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 {
    int x, y;
}e[N];
int n, m, q, f[N], ans[N], v[N];
int od[N], a[N], s[N], z[N];
int fac[N*20], fai[N*20];

int Find(int x) {
    return x == f[x] ? x : (f[x] = Find(f[x]));
}

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;
}

void Pre(int n) {
    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;
}

struct Tree {
    int s, l, r;
}t[N*40];
int root[N], trc;

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

void Merge(int &rt, int x, int l, int r) {
    if (!rt) return rt = x, void();
    t[rt].s += t[x].s;
    int mid = l + r >> 1;
    if (lx) Merge(ls, lx, l, mid);
    if (rx) Merge(rs, rx, mid + 1, r);
}

int Ask(int rt, int l, int r, int x) {
    if (l == r) return t[rt].s;
    int mid = l + r >> 1;
    if (x <= mid) return Ask(ls, l, mid, x);
    else return Ask(rs, mid + 1, r, x);
}

int main() {
    freopen("mc.in", "r", stdin);
    freopen("mc.out", "w", stdout);
    int sum = 0; n = read(); m = read(); q = read();
    for (int i = 1; i <= n; ++i) {
        int k = read(), x = read(); sum += k;
        Change(root[i], 0, n, x, k);
    }
    for (int i = 1; i <= m; ++i)
        e[i] = (Node) {read(), read()};
    for (int i = 1; i <= q; ++i) {
        od[i] = read();
        if (od[i] == 1) {
            a[i] = read(); s[i] = read(); z[i] = read(); sum += s[i];
            if (!z[i]) fputs("No", stderr);
            Change(root[a[i]], 0, n, z[i], s[i]);
        }
        else if (od[i] == 2) a[i] = read(), v[a[i]]++;
        else a[i] = read(), s[i] = read(), z[i] = read();
    }
    Pre(sum);
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 1; i <= m; ++i) {
        if (v[i]) continue;
        int x = Find(e[i].x), y = Find(e[i].y);
        if (x == y) continue;
        if (t[root[x]].s < t[root[y]].s) std::swap(x, y);
        f[y] = x; Merge(root[x], root[y], 0, n);
    }
    for (int i = q; i >= 1; --i) {
        if (od[i] == 1) Change(root[Find(a[i])], 0, n, z[i], -s[i]);
        else if (od[i] == 2) {
            if (--v[a[i]]) continue;
            int x = Find(e[a[i]].x), y = Find(e[a[i]].y);
            if (x == y) continue;
            if (t[root[x]].s < t[root[y]].s) std::swap(x, y);
            f[y] = x; Merge(root[x], root[y], 0, n);
        }
        else {
            int x = Find(a[i]), y = Ask(root[x], 0, n, z[i]);
            sum = t[root[x]].s;
            if (y < s[i]) ans[i] = 0;
            else ans[i] = 1ll * fac[y] * fac[sum-s[i]] % M * fai[y-s[i]] % M * fai[sum] % M;
        }
    }
    for (int i = 1; i <= q; ++i)
        if (od[i] == 3) printf("%d\n", ans[i]);
    return 0;
}

D 简单题

  • 最小生成树后树剖线段树,考场上就剩半小时,就没写出来,有好多细节没注意到
Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)
#define Max(a, b) ({int xx = a, yy = b; xx > yy ? xx : yy;})
#define Min(a, b) ({int xx = a, yy = b; xx < yy ? xx : yy;})

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 Edge {
    int next, t, d, id;
}e[N<<1];
int head[N], edc;

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

struct Node {
    int x, y, d, id;
    bool operator < (const Node &b) const {
        return d < b.d;
    }
}a[N*10];
int n, m, t[N<<2], tag[N<<2], ans[N*10], f[N];
int fa[N], dep[N], son[N], siz[N], c[N];
int tp[N], dfn[N], dfc, id[N];
bool g[N*10];

int Find(int x) {
    return x == f[x] ? x : f[x] = Find(f[x]);
}

void Dfs(int x) {
    siz[x] = 1; dep[x] = dep[fa[x]] + 1;
    for (int i = head[x], y; i; i = e[i].next) {
        if ((y = e[i].t) == fa[x]) continue;
        c[y] = e[i].id; fa[y] = x; 
        Dfs(y); siz[x] += siz[y];
        if (!son[x] || siz[y] > siz[son[x]]) son[x] = y;
    }
}

void Dfs(int x, int top) {
    tp[x] = top; dfn[x] = ++dfc; id[dfc] = x;
    if (!son[x]) return;
    Dfs(son[x], top);
    for (int i = head[x], y; i; i = e[i].next)
        if ((y = e[i].t) != fa[x] && y != son[x]) Dfs(y, y);
}

void Build(int rt, int l, int r) {
    tag[rt] = 1e9;
    if (l == r) return t[rt] = a[c[id[l]]].d, void();
    int mid = l + r >> 1;
    Build(ls, l, mid); Build(rs, mid + 1, r);
    t[rt] = Max(t[ls], t[rs]);
}

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

void Dfs(int rt, int l, int r, int s) {
    s = Min(s, tag[rt]);
    if (l == r) return ans[a[c[id[l]]].id] = s, void();
    int mid = l + r >> 1;
    Dfs(ls, l, mid, s); Dfs(rs, mid + 1, r, s);
}

int main() {
    freopen("easy.in", "r", stdin);
    freopen("easy.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= m; ++i)
        a[i] = (Node) {read(), read(), read(), i};
    std::sort(a + 1, a + m + 1);
    for (int i = 1; i <= n; ++i) f[i] = i;
    for (int i = 1; i <= m; ++i) {
        int x = Find(a[i].x), y = Find(a[i].y);
        if (x == y) continue;
        f[y] = x; g[i] = 1;
        Add(a[i].x, a[i].y, a[i].d, i);
    }
    Dfs(1); Dfs(1, 1); Build(1, 2, n);
    for (int i = 1; i <= m; ++i) {
        if (g[i]) continue;
        int &mx = ans[a[i].id];
        int x = a[i].x, y = a[i].y, d = a[i].d;
        while (tp[x] != tp[y]) {
            if (dep[tp[x]] < dep[tp[y]]) std::swap(x, y);
            mx = Max(mx, Ask(1, 2, n, dfn[tp[x]], dfn[x], d));
            x = fa[tp[x]];
        }
        if (dfn[x] > dfn[y]) std::swap(x, y);
        if (x != y) mx = Max(mx, Ask(1, 2, n, dfn[x] + 1, dfn[y], d));
    }
    Dfs(1, 2, n, 1e9);
    for (int i = 1; i <= m; ++i)
        printf("%d\n", ans[i]);
    return 0;
}



noip晚间小测4

阶段排名 6

A 次芝麻

  • 这个阶段的原题……
Show Code
#include <cstdio>
#include <algorithm>

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

int main() {
    freopen("sesame.in", "r", stdin);
    freopen("sesame.out", "w", stdout);
    int n, m, k;
    scanf("%d%d%d", &n, &m, &k);
    k = 1LL * n * Pow(2, k, n + m) % (n + m);
    printf("%d\n", std::min(k, n + m - k));
    return 0;
}

B 喝喝喝

  • 根号筛因数,双指针扫一遍就行了
Show Code
#include <cstdio>

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;
}

int n, m, a[N], v[N];
long long ans;

int main() {
    freopen("drink.in", "r", stdin);
    freopen("drink.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    for (int i = 1, j = 1; i <= n; ++i) {
        for (; j <= n && ((!v[a[j]] && !v[0]) || a[j] <= m); ++j) {
            int x = a[j] - m;
            if (!x) v[x]++;
            for (int k = 1; k * k <= x; ++k)
                if (x % k == 0) v[k]++, v[x/k]++;
        }
        int x = a[i] - m;
        if (!x) v[x]--;
        for (int k = 1; k * k <= x; ++k)
            if (x % k == 0) v[k]--, v[x/k]--;
        ans += j - i;
    }
    printf("%lld\n", ans);
    return 0;
}

C 长寿花 (Unaccepted)

  • 感觉像是神奇DP就放弃了
Show Code



模拟赛39

阶段排名 10

A 最大K段和

  • 以前老姚讲过,可记不清了,考场上写了个假的贪心但数据太水就A了(直接输出所有正数的和就能A)

  • 可以用线段树维护区间最大子段和,但是好像不是很好写,就没写

  • 还可以用可撤销贪心

  • 将所有相邻的符号相同的都和成一个数,然后将子段个数缩小,可以减去一个正数,也可以把负数加上将相邻的两个合并

  • 实现就是正数取反,然后像种树那样做就好了

Show Code
#include <bits/stdc++.h>

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;
}

std::priority_queue< std::pair<ll, int> > q;
int n, m, l[N], r[N];
ll a[N], ans;
bool v[N];

int main() {
    freopen("maxksum.in", "r", stdin);
    freopen("maxksum.out", "w", stdout);
    n = read(), m = read();
    int cnt = 0;
    for (int i = 1; i <= n; ++i) {
        int x; scanf("%d", &x);
        if (!cnt && x <= 0) continue;
        if (!cnt) a[cnt = 1] = x;
        else if (((x > 0) ^ (a[cnt] > 0)) && x) a[++cnt] = x;
        else a[cnt] += x;
    }
    n = cnt;
    if (a[n] <= 0) n--;
    for (int i = 1; i <= n; ++i) {
        l[i] = i - 1, r[i] = i + 1;
        if (i & 1) q.push(std::make_pair(a[i] = -a[i], i)), ans -= a[i];
        else q.push(std::make_pair(a[i], i));
    }
    l[1] = r[n] = 0;
    for (m = n / 2 + 1 - m; m > 0 && !q.empty(); m--) {
        while (!q.empty() && v[q.top().second]) q.pop();
        if (q.empty()) break;
        ans += q.top().first;
        int x = q.top().second; q.pop();
        if (!l[x]) v[r[x]] = 1, l[r[r[x]]] = 0;
        if (!r[x]) v[l[x]] = 1, r[l[l[x]]] = 0;
        if (l[x] && r[x]) {
            v[l[x]] = v[r[x]] = 1;
            q.push(std::make_pair(a[x] = a[l[x]] + a[r[x]] - a[x], x));
            l[x] = l[l[x]]; r[l[x]] = x;
            r[x] = r[r[x]]; l[r[x]] = x;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

B 双端队列xLIS问题

  • 考场上把倒着模拟了一下,发现了个规律,写的特麻烦

  • 正解就是把序列倒过来放在前面,然后最长上升子序列就是答案

  • 构造其实挺显然的吧

Show Code
#include <cstdio>
#include <algorithm>
#define Max(x, y) (x > y ? x : y)

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;
}

int n, m, a[N*2], b[N], ans, t[N];

void Add(int x, int w) {
    for (; x <= m; x += x & -x)
        t[x] = Max(t[x], w);
}

int Ask(int x, int ans = 0) {
    for (; x; x -= x & -x)
        ans = Max(ans, t[x]);
    return ans;
}

int main() {
    freopen("dequexlis.in", "r", stdin);
    freopen("dequexlis.out", "w", stdout);
    n = read();
    for (int i = n; i >= 1; --i)
        a[i] = b[i] = read();
    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] = a[n+n-i+1]= std::lower_bound(b + 1, b + m + 1, a[i]) - b;
    for (int i = 1, x; i <= n * 2; ++i) {
        Add(a[i], x = Ask(a[i] - 1) + 1);
        ans = Max(ans, x);
    }
    printf("%d\n", ans);
    return 0;
}

C 最大前缀和

  • 类似卡特兰数的推法可以求出最大前缀和小于x的方案数,容斥一下即可求出答案
Show Code
#include <cstdio>

const int N = 4005, M = 998244853;
int n, m, fac[N], fai[N], ans;

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 Init(int n) {
    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;
}

int main() {
    freopen("maxpsum.in", "r", stdin);
    freopen("maxpsum.out", "w", stdout);
    scanf("%d%d", &n, &m);
    Init(n + m);
    for (int i = n; i >= 0 && i >= n - m; --i)
        if ((ans += 1LL * i * (C(n + m, n - i) - C(n + m, n - i - 1) + M) % M) >= M) ans -= M;
    printf("%d\n", ans);
    return 0;
}

D 积木 (Unaccpeted)

  • 概率题,溜了溜了
Show Code



noip晚间小测3

阶段排名 9

A 艰难睡眠

  • 就是求固定区间长度的最小值,单调队列即可,写丑了,题库上TLE成80
Show Code
#include <cstdio>
#define getchar() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, L, stdin), (iS == iT ? EOF : *iS++)) : *iS++)

const int L = (1 << 21) + 1; char ibuf[L], *iS, *iT;
const int N = 5005, M = 2005;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' && c <= '0'; c = getchar())
        if (c == '-') f = -1;
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return x * f;
}

int n, m, k, a[M*2], d[N], q[M*2], ans = 1e9, f[M];

int main() {
    freopen("sleep.in", "r", stdin);
    freopen("sleep.out", "w", stdout);
    n = read(); m = read(); k = m - read();
    for (int i = 1; i <= n; ++i) {
        read(), d[i] = k - read() + 1;
        if (d[i] <= 0) return puts("-1"), 0;
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j)
            a[j] = a[j+m] = read();
        int l = 1, r = 0;
        for (int j = 1; j < m + d[i]; ++j) {
            while (l <= r && q[l] + d[i] <= j) l++;
            while (l <= r && a[q[r]] >= a[j]) r--;
            q[++r] = j;
            if (j >= d[i]) f[j-d[i]+1] += a[q[l]];
        }
    }
    for (int i = 1; i <= m; ++i)
        if (f[i] < ans) ans = f[i];
    printf("%d\n", ans);
    return 0;
}

B 路径难题 (Unaccepted)

  • Dij50分
Show Code



模拟赛38

阶段排名 12

A 理发

  • 考虑一层一层放头发,最多只能放到原来的高度,然后树状数组求出前面有几个放到原来高度的,剩下的就是新增的逆序对数
Show Code
#include <cstdio>
#include <vector>

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;
}

int n, a[N], t[N];
long long ans;
std::vector<int> p[N];

void Add(int x) {
    for (; x <= n; x += x & -x) t[x]++;
}

int Ask(int x, int ans = 0) {
    for (; x; x -= x & -x) ans += t[x];
    return ans;
}

int main() {
    freopen("haircut.in", "r", stdin);
    freopen("haircut.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        p[a[i] = read()].push_back(i);
    for (int i = 0; i < n; ++i) {
        printf("%lld\n", ans);
        for (int j = 0; j < p[i].size(); ++j)
            Add(p[i][j]), ans += p[i][j] - Ask(p[i][j]);
    }
    return 0;
}

B T形覆盖

  • 特殊点向四周建边

  • 并查集维护联通块内特殊点的个数sb,非特殊点的个数sw,特殊点与特殊点间边的个数se,非特殊点边权最小值mi

  • 对于每个联通块:

    1. sw==1:没有与特殊点连边
    2. se==1或0,sw==3sb:一个特殊点对应三个非特殊点
    3. se==0,sw==3sb+1:多一个非特殊点,把点权最小的扔掉即可
    4. 其他情况都是会重叠的,输出No
Show Code
#include <cstdio>
#include <algorithm>

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, a[N], f[N], sw[N], sb[N], se[N], p[N], ans;
bool b[N], v[N];

int Find(int x) {
    return x == f[x] ? x : (f[x] = Find(f[x]));
}

int Get(int x, int od) {
    if (od == 0) return x / m ? x - m : -1;//up
    if (od == 1) return x / m != n - 1 ? x + m : -1;//down
    if (od == 2) return x % m != 0 ? x - 1 : -1;//left
    if (od == 3) return x % m != m - 1 ? x + 1 : -1;//right
}

int main() {
    freopen("t_covering.in", "r", stdin);
    freopen("t_covering.out", "w", stdout);
    n = read(); m = read();
    for (int i = 0; i < n * m; ++i)
        a[i] = read(), f[i] = i, sw[i] = 1;
    int k = read();
    for (int i = 1; i <= k; ++i) {
        int x = read() * m + read();
        p[i] = x;
        b[x] = v[x] = 1;
        sw[x] = 0; sb[x] = 1;
        ans += a[x]; a[x] = 1e9;
    }
    for (int i = 1; i <= k; ++i) {
        int x = p[i];
        for (int j = 0; j < 4; ++j, x = p[i]) {
            int y = Get(x, j); x = Find(x);
            if (y == -1) continue;
            if (b[y]) se[x]++;
            if (!v[y]) ans += a[y], v[y] = 1;
            y = Find(y);
            if (x == y) continue;
            f[y] = x;
            sw[x] += sw[y]; sb[x] += sb[y]; se[x] += se[y];
            a[x] = std::min(a[x], a[y]);
        }
    }
    for (int i = 0; i <= n * m; ++i) {
        if (Find(i) != i) continue;
        if ((se[i] <= 2 && sw[i] == sb[i] * 3) || sw[i] == 1);//判断se==2是因为我一条边算了两遍
        else if (!se[i] && sw[i] == sb[i] * 3 + 1) ans -= a[i];
        else return puts("No"), 0;
    }
    printf("%d\n", ans);
    return 0;
}

C 迷途竹林 (Unaccepted)

  • 打了个前20分的写法,把竹子长m天放堆里,砍km次,每次砍把堆顶砍p再放回去,最后输出堆顶,数据太水,于是它A了
Show Code

D 游戏 (Unaccepted)

  • 写了个搜索结果环到了自己,然后就死了
Show Code



联赛模拟测试37

阶段排名 27

A 简单题

  • 考场上想出a+b+c是定值且q没啥用,但还是没找到规律

  • 观察式子,将A和B加起来就可以把P消掉

  • 然后每次变化就是A+B和C两个数中大的减小的,小的变两倍

  • 如果C小,C变两倍,

  • 如果C大,\(C=C-(A+B)=C-((A+B+C)-C)=2C-(A+B+C)\),C变两倍减去和,和是定值,答案就转换成\(C*2^k\mod (A+B+C)\)

Show Code
#include <cstdio>

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 Pow(int a, int k, int M, int ans = 1) {
    for (; k; k >>= 1, a = 1LL * a * a % M)
        if (k & 1) ans = 1LL * ans * a % M;
    return ans;
}

int main() {
    freopen("easy.in", "r", stdin);
    freopen("easy.out", "w", stdout);
    int T = read();
    while (T--) {
        int x = read() + read(), y = read(), k = read();
        x += y;
        printf("%lld\n", 1LL * y * Pow(2, k, x) % x);
    }
    return 0;
}

B 斗地主

  • 我记得学长讲过,但忘了怎么写了,本来想写60分二分图,然后懒得写,就写了个40分暴搜,有个地方写挂了就只剩20了

  • 每张牌上正面的数向背面的数建双向边,一个联通块如果是树就会有一个点不能选,那么一个区间同时包含这个联通块的最大值和最小值,就不能组成

  • 可以维护一个ans[i]表示从i开始最大能到几,双指针就可以O(n)预处理出这个数组,每次查询就可以做到O(1)了

Show Code
#include <cstdio>

const int N = 2e5 + 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, f[N], s[N], b[N], ans[N];

int Find(int x) {
    return x == f[x] ? x : (f[x] = Find(f[x]));
}

int main() {
    freopen("playingcard.in", "r", stdin);
    freopen("playingcard.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) f[i] = i;
    while (m--) {
        int x = Find(read()), y = Find(read());
        s[x]++;
        if (x == y) continue;
        f[y] = x; s[x] += s[y];
    }
    for (int i = 1, j = 1; i <= n; ++i) {
        for (int x; j <= n && b[x=Find(j)] < s[x]; ++j) b[x]++;
        ans[i] = j - 1; b[Find(i)]--;
    }
    m = read();
    while (m--) {
        int x = read();
        puts(read() <= ans[x] ? "Yes" : "No");
    }
    return 0;
}

C 变化的树

  • 对于x子树上的点y,它增加的值就是w-k(dep[y]-dep[x]) = w+kdep[x]-kdep[y],

  • 对于每个点维护w+kdep[x]的和sw[i]和k的和sk[i],查询的时候就是sw[x]-dep[x]sk[x]

  • 那就拿一个支持区间加,单点查的数据结构,其实树状数组就可以,然而我考场写的大常数线段树

Show Code
#include <cstdio>

const int N = 3e5 + 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 next, t;
}e[N];
int head[N], edc;

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

int n, dep[N], dfc, dfn[N], a[N], siz[N], tw[N], tk[N];

void Dfs(int x, int fa) {
	siz[x] = 1;
	dep[x] = dep[fa] + 1;
	dfn[x] = ++dfc; a[dfc] = x;
	for (int i = head[x]; i; i = e[i].next) {
		int y = e[i].t;
		Dfs(y, x);
		siz[x] += siz[y];
	}
}

void Add(int x, int w, int k) {
	for (; x <= n; x += x & -x) {
		if ((tw[x] += w) >= M) tw[x] -= M;
		if ((tk[x] += k) >= M) tk[x] -= M;
	}
}

int Ask(int x, bool g, int s = 0) {
	for (; x; x -= x & -x)
		if ((s += (g ? tw[x] : tk[x])) >= M) s -= M;
	return s;
}

int main() {
	freopen("change.in", "r", stdin);
	freopen("change.out", "w", stdout);
	n = read();
	for (int i = 2; i <= n; ++i)
		Add(read(), i);
	Dfs(1, 0);
	int m = read();
	while (m--) {
		int od = read(), x = read();
		if (od == 1) {
			int w = read(), k = read(); 
			w = (w + 1LL * dep[x] * k) % M;
			Add(dfn[x], w, k); Add(dfn[x] + siz[x], -w, -k);
		}
		else printf("%lld\n", (Ask(dfn[x], 1) - 1LL * dep[x] * Ask(dfn[x], 0) % M + M) % M);
	}
	return 0;
}

D 炼金术 (Unaccepted)

  • 神仙题爆0
Show Code



posted @ 2020-11-23 18:30  Shawk  阅读(84)  评论(3)    收藏  举报