NOIP第三阶段总结

感觉要菜死了

晚间小测9

阶段排名 16

A 计划

  • 按暴力推个式子就好了
Show Code


#include <cstdio>
#include <algorithm>

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, q, a[N], b[N], cnt, f[N];
long long s[N];

int main() {
	freopen("plan.in", "r", stdin);
	freopen("plan.out", "w", stdout);
	n = read(); m = read(); q = read();
	for (int i = 1; i <= n; ++i)
		a[i] = read();
	for (int i = 1, j = 1; i < n; ++i) {
		for (; j <= n + 1 && cnt < m; ++j) 
			cnt += !b[a[j]]++;
		cnt -= !--b[a[i]]; f[i] = j - 1;
		s[i] = s[i-1] + 1ll * f[i] * (1 - f[i] + i * 2);
	}
	f[n] = n + 1;
	while (q--) {
		int l = read(), r = read(), k = std::upper_bound(f + 1, f + n + 1, r) - f - 1;
		if (k < l) puts("0");
		else printf("%lld
", (s[k] - s[l-1] + (1ll * r * (r + 1) - 1ll * (l + k) * (r + 1)) * (k - l + 1)) / 2);
	}
	return 0;
}

B 赤 (Unaccepted)

Show Code



模拟赛44

阶段排名 16

这几天的题都一点思路都没有,真是难受死了

A 树 (Unaccepted)

  • 一看时限500ms,没敢写(我是不会说我都不会(nlog^3 n)以下的算法的),就打了个最最裸的暴力10分

  • 倍增找到祖先上第一个权值大于自己的并连边,这样就建了一颗新树,然后在新树上倍增。

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

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 n, t;
}e[N<<1];
int h[N], edc;

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

int n, m, a[N], fa[17][N], d[17][N], f[17][N], dep[N];

void Dfs(int x) {
    d[0][x] = a[x];
    dep[x] = dep[fa[0][x]] + 1;
    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);
}

int Find(int x, int w) {
    for (int i = 16; i >= 0; --i)
        if (d[i][x] <= w) x = fa[i][x];
    return x;
}

int main() {
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) 
        a[i] = read();
    for (int i = 1; i < n; ++i) {
        int x = read(), y = read();
        Add(x, y); Add(y, x);
    }
    Dfs(1);// d[0][0] = 1e9;
    for (int i = 0; i < 16; ++i) {
        for (int x = 1; x <= n; ++x) {
            fa[i+1][x] = fa[i][fa[i][x]];
            d[i+1][x] = std::max(d[i][x], d[i][fa[i][x]]);
        }
    }
    for (int i = 1; i <= n; ++i)
        f[0][i] = Find(i, a[i]);
    for (int i = 0; i < 16; ++i)
        for (int x = 1; x <= n; ++x)
            f[i+1][x] = f[i][f[i][x]];
    while (m--) {
        int x = read(), y = read(), w = read(), ans = 1;
        x = Find(x, w);
        if (dep[x] < dep[y]) puts("0");
        else {
            for (int i = 16; i >= 0; --i)
                if (dep[f[i][x]] >= dep[y])
                    x = f[i][x], ans += 1 << i;
            printf("%d
", ans);
        }
    }
    return 0;
}

B 环 (Unaccepted)

  • 题都看不懂,果断puts("0"),成绩也是0
Show Code

C 礼物

  • 最裸暴力走起20分

  • i和j作出的贡献就是从(-a[i],-b[i])走到(a[j],b[j])的方案数,发现这些路径一定会经过y=-x上唯一的一点,所以求出到这条线上每个点的方案数,乘一下就好了

  • 知道思路后还是蛮好写的(我在晚上考试的时候哦实在写不出考试题的时候把这个题写了出来,然后考试结束叫上去就A了)

Show Code
#include <cstdio>

const int N = 2e7 + 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;
}

int n, m, a[100005], b[100005], fac[N], fai[N], f[N*2], 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) {
    return 1ll * fac[n] * fai[m] % M * fai[n-m] % M;
}

int main() {
    freopen("gift.in", "r", stdin);
    freopen("gift.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i) {
        a[i] = read(); b[i] = read();
        if (a[i] + b[i] > m) m = a[i] + b[i];
    }
    fac[0] = 1;
    for (int i = 1; i <= m; ++i)
        fac[i] = 1ll * fac[i-1] * i % M;
    fai[m] = Pow(fac[m], M - 2);
    for (int i = m; i >= 1; --i)
        fai[i-1] = 1ll * fai[i] * i % M;
    for (int i = 1; i <= n; ++i) {
        int s = a[i] + b[i];
        for (int k = 0, j = 2e7 - b[i]; k <= s; ++k, ++j)
            if ((ans += 1ll * f[j] * C(s, k) % M) >= M) ans -= M;
        for (int k = 0, j = 2e7 - a[i]; k <= s; ++k, ++j)
            if ((f[j] += C(s, k)) >= M) f[j] -= M;
    }
    printf("%d
", ans * 2 % M);
    return 0;
}

D 最优排名

  • 开始想到堆贪心,但正确性一直有问题,就搞了n^2的枚举75分

  • 每次给比自己排名高的需要气球数量最少的队气球使其爆零,堆维护,然后单调指针扫刚才比他气球少的队伍是否超过自己,超过就将这个队伍放入堆中

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

typedef long long ll;
const int N = 3e5 + 5;

ll read(ll 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<ll> q;
struct Node {
    ll s, w;
    bool operator < (const Node &b) const {
        return s > b.s;
    }
}a[N];
int n, ans;

int main() {
    freopen("rank.in", "r", stdin);
    freopen("rank.out", "w", stdout);
    ans = n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = (Node) {read(), read()};
    std::sort(a + 2, a + n + 1);
    for (int i = 2; i <= n; ++i) {
        if (a[i].s > a[1].s) q.push(-(a[i].w - a[i].s + 1));
        else ans = i - 1, i = n;
    }
    ll s = a[1].s;
    for (int i = ans + 1, rk = ans; !q.empty() && s >= -q.top(); ans = ans > rk ? rk : ans) {
        s += q.top(), q.pop(), rk--;
        for (; i <= n && a[i].s > s; ++i) q.push(-(a[i].w - a[i].s + 1)), rk++;
    }
    printf("%d
", ans);
    return 0;
}



晚间小测8

阶段排名 13

A antipalindrome

  • 只要保证相邻的不相同,就不会出现偶回文串,只要保证间隔为1的不相同,就不会出现奇回文串
Show Code
#include <cstdio>

const int M = 1e9 + 7;
int T;
long long n, m;
char a[100];

int Pow(int a, long long k, 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("anti.in", "r", stdin);
    freopen("anti.out", "w", stdout);
    scanf("%d", &T);
    while (T--) {
        scanf("%lld%lld", &n, &m);
        if (n == 1) printf("%lld
", m % M);
        else if (n == 2) printf("%lld
", m % M * ((m - 1) % M) % M);
        else printf("%lld
", m % M * ((m - 1) % M) % M * Pow((m - 2) % M, n - 2) % M);
    }
    return 0;
}

B ZZH与计数 (Unaccepted)

  • 好像是矩阵快速幂
Show Code



模拟赛43

阶段排名 14

困死了,前面迷迷糊糊的打完T3T4,然后着了,醒来半个多时T1T2暴力打了打,实在是太难写了

A 糖果机器

  • 写了个二分图跑了个匈牙利,找到匹配,拿了40分

  • 机器人可以接到第i个糖果然后下一次接到第j个糖果,需满足(left | s_j-s_i ight |leq t_i-t_j),绝对值拆开,移项可得
    (egin{cases} t_i+s_ileq t_j+s_j\ t_i-s_ileq t_j-s_j end{cases})

  • 发现是个二维偏序,按第一维排序后求最小最长非下降子序列划分及方案就可以了

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

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 {
    int t, x, w, s;
    Node() {}
    Node(int a, int b) {
        t = a, x = b; s = a + b, w = a - b;
    }
    bool operator < (const Node &b) const {
        return s == b.s ? w < b.w : s < b.s;
    }
}a[N];
int n, f[N], m, b[N];

bool Cmp(const int &x, const int &y) {
    return x > y;
}

int main() {
    freopen("candy.in", "r", stdin);
    freopen("candy.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = Node(read(), read());
    std::sort(a + 1, a + n + 1);
    f[m=1] = a[1].w; b[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (a[i].w < f[m]) f[++m] = a[i].w, b[i] = m;
        else f[b[i] = std::lower_bound(f + 1, f + m + 1, a[i].w, Cmp) - f] = a[i].w;
    }
    printf("%d
", m);
    for (int i = 1; i <= n; ++i)
        printf("%d %d %d
", a[i].x, a[i].t, b[i]);
    return 0;
}

B 手套 (Unaccepted)

  • 看不太懂题,而且是捆绑评测,根本骗不了分
Show Code

C 甲虫 (Unaccepted)

  • 按坐标排序之后(2^n)Dfs,拿了50分
Show Code

D 选举 (Unaccepted)

  • n2暴力20分
Show Code



晚间小测7

阶段排名 18

A ZZH的游戏

  • 一个人走到可以到达的点中最小的点,然后更新另一个人可以到达的点,同时更新答案
Show Code
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

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;
}e1[N<<1], e2[N<<1];
int h1[N], edc1, h2[N], edc2;

void Add1(int x, int y) {
    e1[++edc1] = (Edge) {h1[x], y}; h1[x] = edc1;
}

void Add2(int x, int y) {
    e2[++edc2] = (Edge) {h2[x], y}; h2[x] = edc2;
}

int n, s1, s2;
bool v1[N], v2[N];
std::priority_queue<int> q1, q2;

int main() {
    freopen("game.in", "r", stdin);
    freopen("game.out", "w", stdout);
    int T = read();
    while (T--) {
        n = read();
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            Add1(x, y); Add1(y, x);
        }
        for (int i = 1; i < n; ++i) {
            int x = read(), y = read();
            Add2(x, y); Add2(y, x);
        }
        s1 = read(); s2 = read();
        std::priority_queue<int> q1, q2;
        q1.push(-s1); q2.push(-s2);
        int ans = s1 + s2, m1 = s1, m2 = s2;
        while (1) {
            if (q2.empty() || !q1.empty() && -q1.top() + m2 < -q2.top() + m1) {
                int x = -q1.top(); q1.pop();
                ans = std::max(ans, x + m2);
                if (x < m1) m1 = x; v1[x] = 1;
                for (int i = h1[x]; i; i = e1[i].n)
                    if (!v1[e1[i].t]) q1.push(-e1[i].t);
            }
            else if (!q2.empty()) {
                int x = -q2.top(); q2.pop();
                ans = std::max(ans, x + m1);
                if (x < m2) m2 = x; v2[x] = 1;
                for (int i = h2[x]; i; i = e2[i].n)
                    if (!v2[e2[i].t]) q2.push(-e2[i].t);
            }
            if (m1 == 1 && m2 == 1) break;
        }
        printf("%d
", ans);
        edc1 = edc2 = 0;
        memset(v1, 0, 4 * n + 1);
        memset(h1, 0, 4 * n + 1);
        memset(v2, 0, 4 * n + 1);
        memset(h2, 0, 4 * n + 1);
    }
    return 0;
}

B ZZH与背包

  • 折半搜索, 双指针O(n)查询,这题需要极致的卡常(如果不是hzoi那没事了)
Show Code
#include <cstdio>
#include <algorithm>
#define re register

typedef long long ll;
const int N = 4.2e6;

ll read(re ll x = 0, re char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar());
    for (; c >= '0' && c <= '9'; c = getchar())
        x = x * 10 + c - '0';
    return x;
}

int n, m, n2, v[50], na, nb;
ll a[N], b[N];

void Dfs1(re int x, re ll s) {
    if (x > n2) return a[++na] = s, void();
    Dfs1(x + 1, s); Dfs1(x + 1, s + v[x]);
}

void Dfs2(re int x, re ll s) {
    if (x > n) return b[++nb] = s, void();
    Dfs2(x + 1, s); Dfs2(x + 1, s + v[x]);
}

ll Cal(re ll x, re ll s = 0) {
    for (re int i = 1, j = std::upper_bound(b + 1, b + nb + 1, x) - b - 1; i <= na && j; ++i) {
        for (ll tmp = x - a[i]; j && b[j] > tmp; --j);
        s += j;
    }
    return s;
}

int main() {
    freopen("knapsack.in", "r", stdin);
    freopen("knapsack.out", "w", stdout);
    n = read(), m = read(); n2 = n >> 1;
    for (re int i = 1; i <= n; ++i)
        v[i] = read();
    std::sort(v + 1, v + n + 1);
    if (n2 >= 10) n2 -= 2;
    Dfs1(1, 0); Dfs2(n2 + 1, 0);
    std::sort(a + 1, a + na + 1);
    std::sort(b + 1, b + nb + 1);
    while (m--) {
        re ll l = read(), r = read();
        printf("%lld
", Cal(r) - Cal(l - 1));
    }
    return 0;
}



模拟赛42

阶段排名 24

A 东方记者

  • 暴搜50分

  • dp其实也挺简单的,当时我想的f[i][j]表示到第i个点,移动总距离为j时的收集最多的新闻数,由于j的范围是1e18,就放弃了

  • 正解就是定义为到第i个点,收集到j个新闻走的最短近距离,就听了个定义,10分钟就推出来了

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

const int N = 105;

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], b[N], ans;
long long d, f[N][N];

int main() {
    freopen("news.in", "r", stdin);
    freopen("news.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read(), b[i] = read();
    scanf("%lld", &d);
    memset(f, 0x3f, sizeof(f));
    f[0][0] = 0;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= i; ++j) {
            for (int k = 0; k < i; ++k)
                f[i][j] = std::min(f[i][j], f[k][j-1] + abs(a[i] - a[k]) + abs(b[i] - b[k]));
            if (j > ans && f[i][j] + abs(a[i]) + abs(b[i]) <= d) ans = j;
        }
    }
    printf("%d
", ans);
    return 0;
}

B 琪露诺数

  • 最裸的暴力30分,调了几个小时的高精度,最后数位Dp不会写了

  • 可以不用数位DP,直接找到最大的回文数,就好算了

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

const int N = 205;

struct Bigint {
    short a[N], n;
    Bigint() { memset(a, 0, sizeof(a)); n = 1; }
    void read() {
        char c[N]; scanf("%s", c);
        n = strlen(c);
        for (int i = 1; i <= n; ++i)
            a[i] = c[n-i] - '0';
    }
    void Print() {
        for (int i = n; i >= 1; --i)
            printf("%d", a[i]);
        puts("");
    }
}fac[N], f[N];

Bigint Init(int x) {
    Bigint a; a.n = 0;
    while (x) a.a[++a.n] = x % 10, x /= 10;
    if (!a.n) a.n = 1;
    return a;
}

Bigint operator * (const Bigint &x, const Bigint &y) {
    Bigint s; s.n = x.n + y.n;
    for (int i = 1; i <= x.n; ++i)
        for (int j = 1; j <= y.n; ++j)
            if ((s.a[i+j-1] += x.a[i] * y.a[j]) >= 10)
                s.a[i+j] += s.a[i+j-1] / 10, s.a[i+j-1] %= 10;
    while (s.n >= N || (!s.a[s.n] && s.n > 1)) s.n--;
    return s;
}

Bigint operator - (const Bigint &x, const Bigint &y) {
    Bigint s; s.n = std::max(x.n, y.n);
    for (int i = 1; i <= s.n; ++i)
        if ((s.a[i] += x.a[i] - y.a[i]) < 0)
            s.a[i+1]--, s.a[i] += 10;
    while (s.n >= N || (!s.a[s.n] && s.n > 1)) s.n--;
    return s;
}

Bigint operator + (const Bigint &x, const Bigint &y) {
    Bigint s; s.n = std::max(x.n, y.n) + 1;
    for (int i = 1; i <= s.n; ++i)
        if ((s.a[i] += x.a[i] + y.a[i]) >= 10)
            s.a[i+1]++, s.a[i] -= 10;
    while (s.n >= N || (!s.a[s.n] && s.n > 1)) s.n--;
    return s;
}

int Mod(Bigint &x) {
    int m = 0;
    for (int i = x.n; i >= 1; --i) {
        m = m * 10 + x.a[i];
        x.a[i] = m / 9; m %= 9;
    }
    while (x.n >= N || (!x.a[x.n] && x.n > 1)) x.n--;
    return m;
}

int a[N], n;
bool g;

Bigint Solve(Bigint x) {
    if (x.n == 1 && x.a[1] < 9) return x + Init(1);
    Bigint s; n = 0, g = 1;
    while (x.n != 1 || x.a[1]) a[++n] = Mod(x);
    for (int i = n + 1 >> 1; i >= 1; --i) {
        if (a[i] > a[n-i+1]) i = 1;
        else if (a[i] < a[n-i+1]) g = 0, i = 1;
    }
    for (int i = n; i >= n / 2 + 1; --i)
        s = s * Init(9) + Init(a[i] - (i == n) - (i == n / 2 + 1 && !g) + (i == n / 2 + 1));
    return s + f[n-1];
}

int main() {
    freopen("number.in", "r", stdin);
    freopen("number.out", "w", stdout);
    fac[0] = Init(1), fac[1] = Init(9);
    for (int i = 2; i < N; ++i)
        fac[i] = fac[i-1] * fac[1];
    f[1] = Init(9);
    for (int i = 2; i < N; ++i)
        f[i] = f[i-1] + Init(8) * fac[i-1>>1];
    int T; scanf("%d", &T);
    while (T--) {
        Bigint l, r; l.read(); r.read();
        (Solve(r) - Solve(l - Init(1))).Print();
    }
    return 0;
}

C 御神渡 (Unaccepted)

  • 最小生成树40分
Show Code

D 号元素

  • 写挂了,没开longlong,不过开了也是5分

  • 考虑一个定义f[i]表示从i到当前位置(可以不选当前位置)的最长序列,

  • 在序列里,后面的数一定是前一数后面比他大的第一个数(题目定义就是这样)

  • 那么考虑加上一个数后,之后对前面比他小的数增加1的贡献,知道找到一个大于等于他的数就停止

  • 那么就是从前面第一个大于等于他的数后面的位置到这位置区间加上一

  • 答案就是从i-k+1到i里f[i]的最小值,

  • 所以维护一个支持区间最大值,区间修改的线段树即可

Show Code
#include <cstdio>
#include <algorithm>
#define ls (rt << 1)
#define rs (rt << 1 | 1)

typedef long long ll;
const int N = 1e6 + 5;

ll read(ll 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, k, stk[N], tp, tag[N<<2];
ll a[N], t[N<<2];

void Pushdown(int rt) {
    t[ls] += tag[rt]; tag[ls] += tag[rt];
    t[rs] += tag[rt]; tag[rs] += tag[rt];
    tag[rt] = 0;
}

void Add(int rt, int l, int r, int x, int y) {
    if (x <= l && r <= y) return t[rt]++, tag[rt]++, void();
    int mid = l + r >> 1;
    if (tag[rt]) Pushdown(rt);
    if (x <= mid) Add(ls, l, mid, x, y);
    if (y > mid) Add(rs, mid + 1, r, x, y);
    t[rt] = std::max(t[ls], t[rs]);
}

void Change(int rt, int l, int r, int x) {
    if (l == r) return t[rt] = -1e9, void();
    int mid = l + r >> 1;
    if (tag[rt]) Pushdown(rt);
    if (x <= mid) Change(ls, l, mid, x);
    else Change(rs, mid + 1, r, x);
    t[rt] = std::max(t[ls], t[rs]);
}

int main() {
    freopen("lizard.in", "r", stdin);
    freopen("lizard.out", "w", stdout);
    n = read(); k = read();
    for (int i = 1; i <= n; ++i) {
        a[i] = read();
        while (tp && a[stk[tp]] < a[i]) tp--;
        Add(1, 1, n, stk[tp] + 1, i);
        stk[++tp] = i;
        if (i >= k) {
            printf("%lld ", t[1]);
            Change(1, 1, n, i - k + 1);
        }
    }
    return 0;
}



noip晚间小测6

阶段排名 20

A 蛋糕

  • 区间DP,定义f[i][j]为i到j区间取完获得的最大收益,先选大的转移另外那个人了,然后选两边转移自己的
Show Code
#include <cstdio>
#include <algorithm>

const int N = 4005;

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;
long long f[N][N], ans;

int main() {
    freopen("cake.in", "r", stdin);
    freopen("cake.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i)
        f[i][i] = f[i+n][i+n] = read();
    for (int i = n * 2; i >= 2; --i) {
        for (int j = i; j < n * 2 && j <= i + n - 1; j += 2) {
            if (f[i][j] > ans) ans = f[i][j];
            int x = i - 1, y = j + 1;
            f[x][x] > f[y][y] ? x-- : y++;
            if (x >= 1) f[x][y-1] = std::max(f[x][y-1], f[i][j] + f[x][x]);
            if (y <= n * 2) f[x+1][y] = std::max(f[x+1][y], f[i][j] + f[y][y]);
        }
    }
    printf("%lld
", ans);
    return 0;
}

B 游戏 (Unaccepted)

Show Code



模拟赛41

阶段排名 9

A 四个质数的和

  • 质数才有9592个,n2跑一遍求出组成两个质数的方案数,其实跑不满,就跑25371441次,很快的,计算的时候前两个乘后两个就好了
Show Code
#include <cstdio>

const int N = 1e5 + 5;
int p[N], m, f[N];
bool v[N];

int main() {
    freopen("plus.in", "r", stdin);
    freopen("plus.out", "w", stdout);
    for (int i = 2; i < N; ++i) {
        if (!v[i]) p[++m] = i;
        for (int j = 1; j <= m && i * p[j] < N; ++j) {
            v[i*p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
    for (int i = 1; i <= m && p[i] * 2 < N; ++i) {
        f[p[i]*2]++;
        for (int j = i + 1; j <= m && p[i] + p[j] < N; ++j)
            f[p[i]+p[j]] += 2;
    }
    int T; scanf("%d", &T);
    while (T--) {
        int x; scanf("%d", &x);
        long long s = 0;
        for (int i = 4; i + i < x; ++i)
            s += f[i] * f[x-i] * 2;
        if (!(x & 1)) s += f[x>>1] * f[x>>1];
        printf("%lld
", s);
    }
    return 0;
}

B 匹配最大异或

  • 暴力60分

  • 考虑最高的二进制位,按照最高的二进制位将所有数字分成两个集合(最高位为 0 的一组,最高位为 1 的一组)。

  • 若 x 数列中既有最高位为 0 的,也有最高位为 1 的,则两个集合对应的 p 一定没有交。

  • 否则,两个集合的 p 一定一一对应,这样数列的最高位一定是相同的

  • 然后去掉最高位,将下一位当作最高位

  • 所以分治,两边p一一对应,ans=solve(left)×2,完全不同ans=solve(left)×solve(right),其他情况ans=0

Show Code
#include <cstdio>

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

int n, a[N], v[N], tot;

int Solve(int l, int r) {
    if (l == r) return 1;
    int mid = l + r >> 1, g = 1;
    for (int i = l, j = mid + 1; i <= mid; ++i, ++j)
        if (a[i] != a[j]) g = 0, i = mid;
    if (g) return 2LL * Solve(l, mid) % M;
    g = 1; tot++;
    for (int i = l; i <= mid; ++i) v[a[i]] = tot;
    for (int i = mid + 1; i <= r; ++i)
        if (v[a[i]] == tot) g = 0, i = r;
    if (!g) return 0;
    return 1LL * Solve(l, mid) * Solve(mid + 1, r) % M;
}

int main() {
    freopen("match.in", "r", stdin);
    freopen("match.out", "w", stdout);
    n = read(); read();
    for (int i = 1; i <= (1 << n); ++i)
        a[i] = read();
    printf("%d
", Solve(1, 1 << n));
    return 0;
}

C 染色相邻的边 (Unaccepted)

  • 链的情况写挂了,只有60分
Show Code

D 垃圾分类 (Unaccepted)

  • 最后半小时40分暴力没打出来,输出大样例10分,考完10分钟就写出来了暴力
Show Code



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