NOIP第一阶段总结

阶段总结
这个阶段感觉状态挺好,失误比较少,暴力分拿的也很足

就是考试时感觉的想到正解就很来劲,就算很难写也能写的下去,
没正解思路就会很烦躁,尤其是暴力分又少有难写的时候特别烦,一点也不想写

改题还是太慢,总是改不出来

最后弱弱的问一句,这个阶段的总成绩咋没了

noip晚间小测2

阶段排名

A 袜子分配

  • 打表发现答案是n/(2n-1)
Show Code
#include <cstdio>

int main() {
    freopen("socks.in", "r", stdin);
    freopen("socks.out", "w", stdout);
    int n; scanf("%d", &n);
    printf("%.10lf
", n / (n * 2.0 - 1));
    return 0;
}

B 牛半仙的妹子序列 (Unaccepted)

  • 想了个O(n)的0分错解。。。
Show Code



联赛模拟测试36

阶段排名 4

A party

  • 设天猫猜第i个人(c_i)次,那么这时结束的概率就是(P=prod_{i=1}^{n}(1-(1-p_i)^{c_i}))

  • 由此可见,天猫一定是先把所有的人先猜一遍才可能有结束的可能

  • 而且天猫要尽可能少的次数,所以每次猜要让P变的更大的人

  • 最后期望就是(sum_{i=n}i(P_i-P_{i-1}))

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

int n;
double p = 1;
struct Node {
    double w, s, x;
    bool operator < (const Node &b) const {
        return w < b.w;
    }
}a;
std::priority_queue<Node> q;

int main() {
    freopen("party.in", "r", stdin);
    freopen("party.out", "w", stdout);
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        int x; scanf("%d", &x);
        p *= x / 100.0;
        a.s = a.x = 1 - x / 100.0;
        a.w = (1 - a.s * a.x) / (1 - a.s);
        q.push(a);
    }
    double ans = p * n;
    for (int i = 1 + n; i <= 3e6; ++i) {
        a = q.top(); q.pop();
        double np = p * a.w;
        ans += (np - p) * i;
        p = np;
        a.s *= a.x;
        a.w = (1 - a.s * a.x) / (1 - a.s);
        q.push(a);
    }
    printf("%.10lf
", ans);
    return 0;
}

B fibonotci (Unaccepted)

  • 没判修改的点大于k的情况,50RE成10分
Show Code

C parking

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

const int N = 2e3 + 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 x, y; } a[N];
int n, m, K, f[N][N], ans[N], u[N][N], d[N][N], q1[N], q2[N];
char c[N][N];

int main() {
    freopen("parking.in", "r", stdin);
    freopen("parking.out", "w", stdout);
    n = read(); m = read(); K = read();
    for (int i = 1; i <= n; ++i)
        scanf("%s", c[i] + 1);
    for (int i = 1; i <= K; ++i) {
        int x = read(), y = read();
        c[x][y] = '#';
        a[i-1] = (Node) {x, y};
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            if (c[i][j] == '.') f[i][j] = std::min(f[i-1][j-1], std::min(f[i][j-1], f[i-1][j])) + 1;
            if (f[i][j] > ans[K]) ans[K] = f[i][j];
        }
    }
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            if (c[i][j] == '.') u[i][j] = u[i-1][j] + 1;
    for (int i = n; i >= 1; --i)
        for (int j = 1; j <= m; ++j)
            if (c[i][j] == '.') d[i][j] = d[i+1][j] + 1;
    for (int k = K - 1; k >= 1; --k) {
        int x = a[k].x, y = a[k].y;
        c[x][y] = '.';
        for (int i = x; i <= n && c[i][y] == '.'; ++i)
            u[i][y] = u[i-1][y] + 1;
        for (int i = x; i >= 1 && c[i][y] == '.'; --i)
            d[i][y] = d[i+1][y] + 1;
        for (int l = ans[k+1] + 1; ; ++l) {
            int l1 = 1, r1 = 0, l2 = 1, r2 = 0, m1 = 0, m2 = 0;
            for (int j = 1; j <= m; ++j) {
                while (l1 <= r1 && q1[l1] + l <= j) l1++;
                while (l2 <= r2 && q2[l2] + l <= j) l2++;
                while (l1 <= r1 && u[x][q1[r1]] >= u[x][j]) r1--;
                while (l2 <= r2 && d[x][q2[r2]] >= d[x][j]) r2--;
                q1[++r1] = q2[++r2] = j;
                if (j < l) continue;
                m1 = u[x][q1[l1]];
                m2 = d[x][q2[l2]];
                if (m1 + m2 - 1 >= l) break;
            }
            if (m1 + m2 - 1 < l) { ans[k] = l - 1; break; }
        }
    }
    for (int i = 1; i <= K; ++i)
        printf("%d
", ans[i]);
    return 0;
}

D ant (Unaccepted)

  • 概率跳过
Show Code



联赛模拟测试35

阶段排名 3

A 组合

  • 跑了遍Tarjan然后Dfs,大样例都过不去,拿了30分

  • DFS一遍回溯时放进栈里,把栈输出就行了,无重边自环的时候就是O(n)的

  • 有的话需要当前弧优化

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 next, t;
}e[N<<2];
int head[N], edc;

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

int od, n, m, in[N], out[N], a[N], tot;
bool v[N<<2];

void Dfs(int x) {
//    for (int i = head[x]; i; i = e[i].next) {
    while (head[x]) {
        int i = head[x]; head[x] = e[i].next;//当前弧优化
        if (v[i]) continue;
        v[i] = 1; if (od == 1) v[i^1] = 1;
        Dfs(e[i].t);
        a[++tot] = (od == 1 ? i >> 1 : i);//回溯时记录
        if (od == 1 && (i & 1)) a[tot] = -a[tot];
    }
}

int main() {
    freopen("merge.in", "r", stdin);
    freopen("merge.out", "w", stdout);
    od = read(); n = read(); m = read();
    if (od == 1) edc = 1;
    while (m--) {
        int x = read(), y = read();
        Add(x, y); in[y]++;
        if (od == 1) Add(y, x), in[x]++;
        else out[x]++;
    }
    int s = 0, s1 = 0, s2 = 0, st = 0;
    for (int i = 1; i <= n; ++i) {
        if (!(in[i] + out[i])) continue;
        if (!st) st = i;
        if (od == 1) {//无向图判欧拉路
            if ((in[i] & 1) && (st = i) && ++s > 2) return puts("NO"), 0;
        }
        else {//有向图判欧拉路
            if (in[i] - out[i] > 1 || out[i] - in[i] > 1) return puts("NO"), 0;
            if (out[i] - in[i] == 1 && (st = i) && ++s1 > 1) return puts("NO"), 0;
            if (in[i] - out[i] == 1 && ++s2 > 1) return puts("NO"), 0;
        }
    }
    Dfs(st);
    if (tot != (od == 1 ? edc >> 1 : edc)) return puts("NO"), 0;//图可能不联通
    puts("YES");
    for (int i = tot; i >= 1; --i)
        printf("%d", a[i]), printf(i == 1 ? "
" : " ");
    return 0;
}

B 小W的魔术

  • 其实字符串是啥没啥影响,只需要字符串的长度

  • 考虑什么样的不能变成喜欢的串,就是前缀和后缀拼起来都组不成喜欢的串

  • 枚举有k位在前面,则k+1位放一个除了a[k+1]之外的字符,最后len-k位放不是a剩下的字符串,有(2^{len-k}-1)种情况,中间的26个字符随便放

  • 考场上特判n==len的情况时return了printf,然后RE了15分

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

const int N = 1e6 + 5, M = 998244353;
long long n;
int ans, len, p[N], P;
char a[N];

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("magic.in", "r", stdin);
    freopen("magic.out", "w", stdout);
    scanf("%lld%s", &n, a + 1);
    len = strlen(a + 1);
    p[0] = 1;
    for (int i = 1; i <= len; ++i)
        p[i] = 1LL * p[i-1] * 26 % M;
    if (n == len) return printf("%d
", p[n] - 1), 0;
    P = Pow(26, n - len - 1);
    for (int i = 1; i <= len; ++i)
        if ((ans += 25LL * P % M * (p[i] - 1) % M) >= M) ans -= M;
    printf("%d
", ans);
    return 0;
}

C 小Y的图

  • 建出最小生成树,问题转换为树上的路径x,y上边权的最大值,倍增维护一段的最大值,边跳lca边求就好了
Show Code
#include <cstdio>
#include <algorithm>

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

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

struct Node {
    int x, y, z;
    bool operator < (const Node &b) const {
        return z < b.z;
    }
}a[N];
int n, m, q, f[N], s, dep[N], fa[20][N], d[20][N];

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

void Dfs(int x) {
    dep[x] = dep[fa[0][x]] + 1;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (y == fa[0][x]) continue;
        fa[0][y] = x, d[0][y] = e[i].d;
        Dfs(y);
    }
}

int Cal(int x, int y) {
    if (Find(x) != Find(y)) return -1;
    int ans = 0;
    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) ans = std::max(ans, d[i][x]), x = fa[i][x];
    if (x == y) return ans;
    for (int i = 19; i >= 0; --i) {
        if (fa[i][x] == fa[i][y]) continue;
        ans = std::max(ans, d[i][x]), x = fa[i][x];
        ans = std::max(ans, d[i][y]), y = fa[i][y];
    }
    return std::max(ans, std::max(d[0][x], d[0][y]));
}

int main() {
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    n = read(); m = read(); q = read();
    for (int i = 1; i <= m; ++i)
        a[i] = (Node) {read(), read(), read()};
    for (int i = 1; i <= n; ++i) f[i] = i;
    std::sort(a + 1, a + m + 1);
    for (int i = 1; i <= m; ++i) {
        int x = Find(a[i].x), y = Find(a[i].y);
        if (x == y) continue;
        f[x] = y;
        Add(a[i].x, a[i].y, a[i].z);
        Add(a[i].y, a[i].x, a[i].z);
        if (++s > n - 1) break;
    }
    for (int i = 1; i <= n; ++i)
        if (!dep[i]) Dfs(i);
    for (int i = 0; i < 19; ++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]]);
        }
    }
    while (q--) printf("%d
", Cal(read(), read()));
    return 0;
}

D 小L的数 (Unaccepted)

  • 60分:把1e13内所有喜欢的数放在哈希表里,总共6e5多点,不是1不是2就输出3
Show Code



noip晚间小测1

阶段排名 6

A 牛半仙的妹子图

  • 很容易想到一个房子被加入的时候的困难程度就是起点到他的所有路径中边权最大值最小的值,这个SPFA求一下就行

  • 困难程度范围为1e9,但其实很多连续的都是相同的,s维护一下前缀和,离散化什么的搞一搞就好了

  • 考场上有些迷糊,最后那里二分的地方没调出来

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

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

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

std::queue<int> q;
std::vector<int> c[N];
int n, m, Q, u, od, M, a[N], f[N], b[N], tot, t[N], cnt[N];
long long s[N], ans;
bool v[N];

long long Cal(int x) {
    int y = std::upper_bound(b + 1, b + tot + 1, x) - b - 1;
    return s[y] + 1LL * cnt[y] * (x - b[y]);
}

int main() {
    freopen("graph.in", "r", stdin);
    freopen("graph.out", "w", stdout);
    n = read(); m = read();
    Q = read(); u = read();
    if (od = read()) M = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 1; i <= m; ++i) {
        int x = read(), y = read(), z = read();
        Add(x, y, z); Add(y, x, z);
    }
    memset(f, 0x3f, sizeof(f));
    q.push(u); f[u] = 0;
    while (!q.empty()) {//SPFA
        int x = q.front(); 
        q.pop(); v[x] = 0;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].t, d = std::max(f[x], e[i].d);
            if (f[y] <= d) continue;
            f[y] = d;
            if (!v[y]) q.push(y), v[y] = 1;
        }
    }
    memcpy(b + 1, f + 1, 4 * n);
    std::sort(b + 1, b + n + 1);
    tot = std::unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; i ++)
        f[i] = std::lower_bound(b + 1, b + tot + 1, f[i]) - b;
    for (int i = 1; i <= n; ++i)
        c[f[i]].push_back(i);
    for (int i = 1; i <= tot; ++i) {
        cnt[i] = cnt[i-1];
        for (int j = 0; j < c[i].size(); ++j)
            cnt[i] += !t[a[c[i][j]]]++;
        s[i] = s[i-1] + 1LL * cnt[i-1] * (b[i] - b[i-1] - 1) + cnt[i];
    }
    while (Q--) {
        int l = read(), r = read();
        if (od) {
            l = (ans ^ l) % M + 1;
            r = (ans ^ r) % M + 1;
            if (l > r) std::swap(l, r);
        }
        printf("%lld
", ans = Cal(r) - Cal(l - 1));
    }
    return 0;
}

B 牛半仙的妹子Tree

  • 最暴力的扩展40分,写着写着就忘了是颗树了

  • 换一种暴力,把最先开始无视的记录下来,每次比较距离和时间差,复杂度O(nm),但数据太水,于是他A了

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

struct Edge {
    int next, t;
}e[N<<1];
int head[N], edc;

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

int n, m, a[N], t[N], tot, dep[N], fa[N], son[N], siz[N], tp[N];

void Dfs(int x) {
    siz[x] = 1;
    dep[x] = dep[fa[x]] + 1;
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (y == fa[x]) continue;
        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;
    if (!son[x]) return;
    Dfs(son[x], top);
    for (int i = head[x]; i; i = e[i].next) {
        int y = e[i].t;
        if (y == fa[x] || y == son[x]) continue;
        Dfs(y, y);
    }
}

int Lca(int x, int y) {
    while (tp[x] != tp[y])
        dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
    return dep[x] < dep[y] ? x : y;
}

bool Judge(int x, int ti) {
    for (int i = 1; i <= tot; ++i)
        if (dep[x] + dep[a[i]] - dep[Lca(x, a[i])] * 2 <= ti - t[i])
            return 1;
    return 0;
}

int main() {
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i < n; ++i) {
        int x = read(), y = read();
        Add(x, y); Add(y, x);
    }
    Dfs(1); Dfs(1, 1);
    for (int i = 1; i <= m; ++i) {
        int od = read(), x = read();
        if (od == 2) tot = 0;
        else if (od == 3) puts(Judge(x, i) ? "wrxcsd" : "orzFsYo");
        else if (!Judge(x, i)) a[++tot] = x, t[tot] = i;
    }
    return 0;
}



联赛模拟测试34

阶段排名 6

A 666

  • 打表发现只有复制后粘贴1,2,4,6次,加上删一个的操作,总共有5种转移,队列维护就行了,开3倍就够了
Show Code
#include <cstdio>
#include <cstring>
#define re register

const int N = 1e6 + 5;
int n, f[N], q[N*3], l, r, a[] = {2, 3, 5, 7};

int main() {
    freopen("six.in", "r", stdin);
    freopen("six.out", "w", stdout);
    scanf("%d", &n);
    memset(f, 0x3f, sizeof(f));
    f[0] = 1, f[1] = 0;
    q[l=r=1] = 1;
    while (l <= r) {
        re int x = q[l++];
        for (re int i = 0; i < 4; ++i)
            if (x * a[i] < N && f[x*a[i]] > f[x] + a[i])
                f[x*a[i]] = f[x] + a[i], q[++r] = x * a[i];
        if (x && f[x-1] > f[x] + 1) 
        f[x-1] = f[x] + 1, q[++r] = x - 1;
    }
    printf("%d
", f[n]);
    return 0;
}

B 春思

  • 数论题,质因数分解
Show Code
#include <cstdio>

typedef long long ll;
const int N = 1e6 + 5, M = 9901;
int prime[N], tot, s[N], cnt, ans = 1;
long long a[N];
bool v[N];

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

int main() {
    freopen("spring.in", "r", stdin);
    freopen("spring.out", "w", stdout);
    for (int i = 2; i < N; ++i) {
        if (!v[i]) prime[++tot] = i;
        for (int j = 1; j <= tot && i * prime[j] < N; ++j) {
            v[i*prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    long long n, m;
    scanf("%lld%lld", &n, &m);
    for (int i = 1; 1LL * prime[i] * prime[i] <= n; ++i) {
        if (n % prime[i]) continue;
        a[++cnt] = prime[i];
        for (; n % prime[i] == 0; n /= prime[i]) s[cnt]++; 
    }
    if (n > 1) a[++cnt] = n, s[cnt] = 1;
    for (int i = 1; i <= cnt; ++i) {
        if (a[i] % M == 1) s[i] = (m * s[i] + 1) % M;
        else s[i] = 1LL * (Pow(a[i] % M, m * s[i] + 1) - 1) * Pow((a[i] - 1) % M, M - 2) % M;
    }
    for (int i = 1; i <= cnt; ++i)
        ans = 1LL * ans * s[i] % M;
    printf("%d
", ans);
    return 0;
}

C 密州盛宴

  • 1比0多的情况把前面的1给乡亲们吃一定不劣,然后只有前面0的个数大于等于1的个数才能放1

  • 问题其实可以转换成至少从后面移几盘0到最前面,将0看作-1,求出最大前缀和减去1与0的数量差减1就是答案

Show Code
#include <cstdio>
#include <cstring>
#define int long long

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

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

signed main() {
    freopen("meal.in", "r", stdin);
    freopen("meal.out", "w", stdout);
    while (1) {
        int n = read(), m = read(), s = 0, ans = 0;
        if (!n && !m) break;
        while (m--) {
            scanf("%s", a);
            int t = read(), sum = 0, mx = -1e9, l = strlen(a);
            for (int i = 0; i < l; ++i) {
                sum += a[i] == '1' ? 1 : -1;
                if (sum > mx) mx = sum;
            }
            if (ans < s + sum * t) ans = s + sum * t;
            if (sum > 0 && ans < s + sum * (t - 1) + mx) ans = s + sum * (t - 1) + mx;
            if (sum <= 0 && ans < s + mx) ans = s + mx; 
            s += sum * t;
        }
        if (s < 0) { puts("-1"); continue;}
        ans = ans - s - 1;
        printf("%lld
", ans > 0 ? ans : 0LL);
    }
    return 0;
}

D 赤壁情 (Unaccepted)

  • 神仙DP不会
Show Code



联赛模拟测试33

阶段排名 1

A 合并集合

  • 环状所以复制一遍放在后面,区间DP一下就好了,f[i][j]表示区间i到j所获收益的最大值,枚举断点转移

  • s[i][j]表示将区间i到j的集合的元素个数,就是不相同的数的个数,考场上用的bitset,其实开个桶,n2扫一遍就行了

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

const int N = 605;

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

int n, s[N][N], f[N][N], ans, a[N], b[N<<1];

int main() {
    freopen("merge.in", "r", stdin);
    freopen("merge.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i) 
        a[i] = a[i+n] = read();
    for (int i = 1; i <= n + n; ++i)
        for (int j = i; j <= n + n && j <= i + n - 1; ++j)
            s[i][j] = s[i][j-1] + (b[a[j]] != i), b[a[j]] = i;
    for (int d = 2; d <= n; ++d)
        for (int i = 1, j; (j = i + d - 1) < n * 2; ++i)
            for (int k = i; k < j; ++k)
                f[i][j] = std::max(f[i][j], f[i][k] + f[k+1][j] + s[i][k] * s[k+1][j]);
    for (int i = 1; i <= n; ++i)
        ans = std::max(ans, f[i][i+n-1]);
    printf("%d
", ans);
    return 0;
}

ZYB建围墙

  • 找规律,假设中间一个为第0层,每扩一层增加6个墙,在第k层的时候加第一个墙会多出k-1个空位,加最后一个墙会多出k+1个空位,其他都是加一个墙多出k个空位

  • 根据等差数列求和公式其实可以O(1)求解,但跑一遍也慢不到哪去

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

const int N = 605;

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

int n, s[N][N], f[N][N], ans, a[N], b[N<<1];

int main() {
    freopen("merge.in", "r", stdin);
    freopen("merge.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i) 
        a[i] = a[i+n] = read();
    for (int i = 1; i <= n + n; ++i)
        for (int j = i; j <= n + n && j <= i + n - 1; ++j)
            s[i][j] = s[i][j-1] + (b[a[j]] != i), b[a[j]] = i;
    for (int d = 2; d <= n; ++d)
        for (int i = 1, j; (j = i + d - 1) < n * 2; ++i)
            for (int k = i; k < j; ++k)
                f[i][j] = std::max(f[i][j], f[i][k] + f[k+1][j] + s[i][k] * s[k+1][j]);
    for (int i = 1; i <= n; ++i)
        ans = std::max(ans, f[i][i+n-1]);
    printf("%d
", ans);
    return 0;
}

ZYB和售货机

  • 考场上想的不太对,找到环后将最小的贡献减去,拿了20分

  • 首先要找到每个物品花费最小的按钮,发现不在环上的就全选上,在环上的就会有一种物品中的一个不选择环上的按钮,也就是次小的按钮

  • 一个点如果不是最优决策,那它一定可以买完,统计到答案里。但如果它有可选的儿子,在tarjan中就会算重,所以要用v数组标记一下

Show Code
#include <cstdio>
#include <vector>
#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;
}

std::vector<int> s[N];
int n, f[N], b[N], w[N], a[N], t[N];
int dfc, dfn[N], low[N], stk[N], tp;
long long ans;
bool c[N], v[N];

void Tarjan(int x) {
    dfn[x] = low[x] = ++dfc;
    stk[++tp] = x;
    if (t[x]) {
        if (!dfn[t[x]]) Tarjan(t[x]), low[x] = std::min(low[x], low[t[x]]);
        else if (!c[t[x]]) low[x] = std::min(low[x], dfn[t[x]]);
    }
    if (dfn[x] == low[x]) {
        int siz = 0, mi = 1e9;
        while (1) {
            int y = stk[tp--];
            if (s[y].size() && !v[y]) {
                ans += 1LL * std::max(0, w[y] - b[s[y][0]]) * a[y];
                mi = std::min(mi, w[y] - b[s[y][0]]);
                if (s[y].size() > 1 && s[y][1] < w[y]) 
                    mi = std::min(mi, b[s[y][1]] - b[s[y][0]]);
            }
            c[y] = 1; siz++;
            if (y == x) break;
        }
        if (siz > 1) ans -= mi;
    }
}

bool Cmp(const int &x, const int &y) {
    return b[x] < b[y];
}

int main() {
    freopen("goods.in", "r", stdin);
    freopen("goods.out", "w", stdout);
    n = read();
    for (int i = 1; i <= n; ++i) {
        f[i] = read(), b[i] = read(), w[i] = read(), a[i] = read();
        s[f[i]].push_back(i);
    }
    for (int i = 1; i <= n; ++i)
        std::sort(s[i].begin(), s[i].end(), Cmp);
    for (int i = 1; i <= n; ++i) {
        if (s[f[i]][0] != i && s[i].size()) 
            ans += 1LL * std::max(0, w[i] - b[s[i][0]]) * a[i], v[i] = 1;
        else if (s[f[i]][0] == i && w[f[i]] > b[i]) t[i] = f[i];
    }
    for (int i = 1; i <= n; ++i)
        if (!dfn[i] && t[i]) Tarjan(i);
    printf("%lld
", ans);
    return 0;
}

ZYB玩字符串

  • 枚举长度,枚举起点,将这些字符串先按字典序排序,检验的时候就从前往后,能删就删,拿了50分,不排序,能拿70分

  • 正解是DP,定义f[l][r]表示区间l,r可否被全消掉或恰好剩一个前缀,记忆化会快一些

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

typedef unsigned long long ull;
const int N = 205;
char c[N];
int T, n, m, d, l[N], r[N], f[N][N];
bool g;

struct Node {
    char c[N];
}a[N];

bool Cmp(const Node &a, const Node &b) {
    for (int i = 0; i < d; ++i)
        if (a.c[i] < b.c[i]) return 1;
    return 0;
}

bool eq(const Node &a, const Node &b) {
    for (int i = 0; i < d; ++i)
        if (a.c[i] != b.c[i]) return 0;
    return 1;
}

bool Dfs(const char *a, int l, int r) {
    int &s = f[l][r];
    if (s != -1) return s;
    if (l > r) return s = 1;
    for (int i = r - d; i >= l; i -= d)
        if (Dfs(a, l, i) && Dfs(a, i + 1, r))
            return s = 1;
    if (c[r] == a[(r-l)%d]) return s = Dfs(a, l, r - 1);
    return s = 0;
}

int main() {
    freopen("string.in", "r", stdin);
    freopen("string.out", "w", stdout);
    scanf("%d
", &T);
    while (T--) {
        scanf("%s", c + 1);
        n = strlen(c + 1);
        for (d = 1, g = 0; d <= n && !g; ++d) {
            if (n % d) continue;
            for (int i = 1; i + d - 1 <= n; ++i)
                memcpy(a[i].c, c + i, sizeof(char) * d), a[i].c[d] = c[n+1];
            a[n-d+2].c[0] = a[n+1].c[0] = 0;
            for (int i = 1; i <= n - d + 1; ++i) {
                memset(f, -1, sizeof(f));
                if (Dfs(a[i].c, 1, n) && (!a[n+1].c[0] || Cmp(a[i], a[n+1]))) 
                    memcpy(a[n+1].c, a[i].c, sizeof(char) * d), g = 1;
            }
            a[n+1].c[d] = 0;
            if (g) printf("%s
", a[n+1].c);
        }
    }
    return 0;
}



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