首页 > 代码库 > NOIP2016题目整合

NOIP2016题目整合

今天终于拿到官方数据,兴致勃勃地全 A 了。

Day 1 T1 toy

处理一下正负号加加减减取模乱搞就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 100010
int n, q;
struct Toy { int tp; char S[15]; } ts[maxn];

int main() {
	freopen("toy.in", "r", stdin);
	freopen("toy.out", "w", stdout);
    n = read(); q = read();
    for(int i = 0; i < n; i++) {
        int t = read(); scanf("%s", ts[i].S);
        if(!t) ts[i].tp = 1;
        else ts[i].tp = -1;
    }
    int p = 0;
    while(q--) {
        int a = read(), b = read();
        if(a) a = 1; else a = -1;
        p += a * ts[p].tp * b;
        p = (p % n + n) % n;
    }
    
    printf("%s\n", ts[p].S);
    
    return 0;
}

Day 1 T2 running

受到“链”和“S 恒为 1”和“T 恒为 1”的特殊点的启发,我们发现可以将每条链 [Si, Ti] 分成 [Si, Ci] 和 [Ci, Ti] 两条,然后对于一个全部可观测到的链 [Ci, Ti] 将所有 Wi 减去深度是一个定值,对于 [Si, Ci] 的部分所有 Wi 加上深度是一个定值。于是树链剖分再打打标记统计就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 300010
#define maxm 600010
#define maxs 12000010
int n, q, m, head[maxn], next[maxm], to[maxm], W[maxn];
struct Player {
    int s, t, c;
    Player() {}
    Player(int _, int __): s(_), t(__) {}
} ps[maxn];

void AddEdge(int a, int b) {
    to[++m] = b; next[m] = head[a]; head[a] = m;
    swap(a, b);
    to[++m] = b; next[m] = head[a]; head[a] = m;
    return ;
}

int fa[maxn], dep[maxn], siz[maxn], son[maxn], top[maxn], pos[maxn], pid[maxn], clo;
void build(int u) {
    siz[u] = 1;
    for(int e = head[u]; e; e = next[e]) if(to[e] != fa[u]) {
        fa[to[e]] = u;
        dep[to[e]] = dep[u] + 1;
        build(to[e]);
        siz[u] += siz[to[e]];
        if(siz[son[u]] < siz[to[e]]) son[u] = to[e];
    }
    return ;
}
void gett(int u, int tp) {
    top[u] = tp; pid[++clo] = u; pos[u] = clo;
    if(son[u]) gett(son[u], tp);
    for(int e = head[u]; e; e = next[e])
        if(to[e] != fa[u] && to[e] != son[u]) gett(to[e], to[e]);
    return ;
}
int lca(int a, int b) {
    int f1 = top[a], f2 = top[b];
    while(f1 != f2) {
        if(dep[f1] < dep[f2]) swap(f1, f2), swap(a, b);
        a = fa[f1]; f1 = top[a];
    }
    return dep[a] < dep[b] ? a : b;
}

struct Info {
    int c, fir[maxn], aft[maxs], val[maxs];
    void clear() {
        c = 0;
        memset(fir, 0, sizeof(fir));
        return ;
    }
    void AddInfo(int x, int v) {
        val[++c] = v; aft[c] = fir[x]; fir[x] = c;
        return ;
    }
} add, del;
int tot[maxn<<1], ans[maxn];
void process(int x, int t, int a, int v) {
    int f = top[a];
    while(f != top[t]) {
        add.AddInfo(pos[f], v);
        del.AddInfo(pos[a], v);
        a = fa[f]; f = top[a];
    }
    add.AddInfo(pos[t], v);
    del.AddInfo(pos[a], v);
    return ;
}

int main() {
	freopen("running.in", "r", stdin);
	freopen("running.out", "w", stdout);
    n = read(); q = read();
    for(int i = 1; i < n; i++) {
        int a = read(), b = read();
        AddEdge(a, b);
    }
    for(int i = 1; i <= n; i++) W[i] = read();
    for(int i = 1; i <= q; i++) {
        int s = read(), t = read();
        ps[i] = Player(s, t);
    }
    build(1);
    gett(1, 1);
    
    for(int i = 1; i <= n; i++) W[i] -= dep[i];
    add.clear(); del.clear(); memset(tot, 0, sizeof(tot));
    for(int i = 1; i <= q; i++) {
        ps[i].c = lca(ps[i].s, ps[i].t);
        process(i, ps[i].c, ps[i].t, dep[ps[i].s] - dep[ps[i].c] - dep[ps[i].c]);
    }
    for(int i = 1; i <= n; i++) {
        for(int e = add.fir[i]; e; e = add.aft[e]) {
            int v = add.val[e] + n;
            tot[v]++;
        }
        int u = pid[i];
        ans[u] += tot[W[u]+n];
        for(int e = del.fir[i]; e; e = del.aft[e]) {
            int v = del.val[e] + n;
            tot[v]--;
        }
    }
    
    for(int i = 1; i <= n; i++) W[i] += (dep[i] << 1);
    add.clear(); del.clear(); memset(tot, 0, sizeof(tot));
    for(int i = 1; i <= q; i++)
        process(i, ps[i].c, ps[i].s, dep[ps[i].s]);
    for(int i = 1; i <= n; i++) {
        for(int e = add.fir[i]; e; e = add.aft[e]) {
            int v = add.val[e];
            tot[v]++;
        }
        int u = pid[i];
        ans[u] += tot[W[u]];
        for(int e = del.fir[i]; e; e = del.aft[e]) {
            int v = del.val[e];
            tot[v]--;
        }
    }
    
    for(int i = 1; i <= q; i++)
        if(W[ps[i].c] == dep[ps[i].s]) ans[ps[i].c]--;
    
    for(int i = 1; i <= n; i++) printf("%d%c", ans[i], i < n ? ‘ ‘ : ‘\n‘);
    
    return 0;
}

Day 1 T3 classroom

这题我考场上想到正解但是因为邻接矩阵连边时忘记取 min 就 sb 了。。。。。

设 f[0][i][j] 表示前 i 条请求使用了 j 条,最后一条没取的最小期望距离;f[1][i][j] 表示前 i 条请求使用了 j 条,最后一条取了的最小期望距离。然后因为每次走哪条边是独立的,转移时直接乘上概率累加上就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 2010
#define maxv 310
#define oo 1000000000
int n, m, v, e, c[maxn], d[maxn], D[maxn][maxn];
double p[maxn], f[2][maxn][maxn];

void up(double& a, double b) {
    a = min(a, b);
    return ;
}

int main() {
	freopen("classroom.in", "r", stdin);
	freopen("classroom.out", "w", stdout);
    n = read(); m = read(); v = read(); e = read();
    for(int i = 1; i <= n; i++) c[i] = read();
    for(int i = 1; i <= n; i++) d[i] = read();
    for(int i = 1; i <= n; i++) scanf("%lf", &p[i]);
    for(int i = 1; i <= v; i++) {
        D[i][i] = 0;
        for(int j = i + 1; j <= v; j++)
            D[i][j] = D[j][i] = oo;
    }
    for(int i = 1; i <= e; i++) {
        int a = read(), b = read(), c = read();
        D[a][b] = min(D[a][b], c);
        D[b][a] = min(D[b][a], c);
    }
    for(int k = 1; k <= v; k++)
        for(int i = 1; i <= v; i++)
            for(int j = 1; j <= v; j++)
                D[i][j] = min(D[i][j], D[i][k] + D[k][j]);
    
    for(int i = 0; i <= n; i++)
        for(int j = 0; j <= m; j++) f[0][i][j] = f[1][i][j] = 1e9;
    f[0][0][0] = 0.0;
    for(int i = 0; i <= n; i++)
        for(int j = 0; j <= min(i + 1, m); j++) {
            double P = p[i], np = p[i+1], p1 = 1.0 - P, np1 = 1.0 - np;
            up(f[0][i+1][j], f[0][i][j] + D[c[i]][c[i+1]]);
            up(f[1][i+1][j+1], f[0][i][j] + np * D[c[i]][d[i+1]] + np1 * D[c[i]][c[i+1]]);
            up(f[0][i+1][j], f[1][i][j] + P * D[d[i]][c[i+1]] + p1 * D[c[i]][c[i+1]]);
            up(f[1][i+1][j+1], f[1][i][j] + P * np * D[d[i]][d[i+1]] + P * np1 * D[d[i]][c[i+1]] + p1 * np * D[c[i]][d[i+1]] + p1 * np1 * D[c[i]][c[i+1]]);
        }
    
    double ans = 1e9;
    for(int i = 0; i <= m; i++) up(ans, min(f[0][n][i], f[1][n][i]));
    printf("%.2lf\n", ans);
    
    return 0;
}

Day 2 T1 problem

用递推法求组合数,实时模 k。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 2010
int C[2][maxn], f[maxn][maxn];

int main() {
	freopen("problem.in", "r", stdin);
	freopen("problem.out", "w", stdout);
    int size = 2000;
    bool cur = 0;
    int T = read(), k = read();
    for(int i = 0; i <= size; i++, cur ^= 1) {
        C[cur][0] = 1; C[cur][i] = 1;
        for(int j = 1; j < i; j++) C[cur][j] = (C[cur^1][j-1] + C[cur^1][j]) % k;
        for(int j = 0; j <= i; j++) {
            f[i][j] = (!C[cur][j]);
            int t = 0;
            if(j) f[i][j] += f[i][j-1], t++;
            if(j <= i - 1) f[i][j] += f[i-1][j], t++;
            if(t == 2 && i && j) f[i][j] -= f[i-1][j-1];
        }
    }
    while(T--) {
        int n = read(), m = read();
        printf("%d\n", f[n][min(n,m)]);
    }
    
    return 0;
}

Day 2 T2 earthworm

受到 q = 0 即蚯蚓长度没有增加的数据启发,我们发现每次坎出的两条蚯蚓的长度一定是单调降的,于是可以 O(n) 做了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 100010
#define maxm 7100010
#define LL long long
int n, m, q, u, v, t, A[maxn], B[maxm], C[maxm], lb, rb, lc, rc, ans[maxm], cnt;

bool cmp(int a, int b) { return a > b; }

void process(int tmp, int i, int len) {
    int nb = (LL)tmp * u / v, nc = tmp - nb;
    B[++rb] = nb - len - q;
    C[++rc] = nc - len - q;
    if(i % t == 0) ans[++cnt] = tmp;
    return ;
}

int main() {
	freopen("earthworm.in", "r", stdin);
	freopen("earthworm.out", "w", stdout);
    n = read(); m = read(); q = read(); u = read(); v = read(); t = read();
    for(int i = 1; i <= n; i++) A[i] = read();
    sort(A + 1, A + n + 1, cmp);
//    for(int i = 1; i <= n; i++) printf("%d%c", A[i], i < n ? ‘ ‘ : ‘\n‘);
    
    lb = 1; rb = 0; lc = 1; rc = 0;
    int pa = 1, len = 0;
    for(int i = 1; i <= m; i++, len += q) {
        int a, b, c;
        a = (pa <= n) ? A[pa] + len : -1;
        b = (lb <= rb) ? B[lb] + len : -1;
        c = (lc <= rc) ? C[lc] + len : -1;
        if(a >= b && a >= c) {
            pa++;
            process(a, i, len);
        }
        else if(b >= a && b >= c) {
            lb++;
            process(b, i, len);
        }
        else {
            lc++;
            process(c, i, len);
        }
    }
    for(int i = 1; i <= cnt; i++) printf("%d%c", ans[i], i < cnt ? ‘ ‘ : ‘\n‘);
    if(!cnt) putchar(‘\n‘);
    cnt = 0;
    for(int i = 1; i <= n + m; i++) {
        int a, b, c;
        a = (pa <= n) ? A[pa] + len : -1;
        b = (lb <= rb) ? B[lb] + len : -1;
        c = (lc <= rc) ? C[lc] + len : -1;
        if(a >= b && a >= c) {
            pa++;
            if(i % t == 0) ans[++cnt] = a;
        }
        else if(b >= a && b >= c) {
            lb++;
            if(i % t == 0) ans[++cnt] = b;
        }
        else {
            lc++;
            if(i % t == 0) ans[++cnt] = c;
        }
    }
    for(int i = 1; i <= cnt; i++) printf("%d%c", ans[i], i < cnt ? ‘ ‘ : ‘\n‘);
    if(!cnt) putchar(‘\n‘);
    
    return 0;
}

Day 2 T3 angrybirds

状压 dp,设 f[S] 表示干掉集合 S 的猪需要的最少抛物线条数,转移时找到第一个没有被干掉的猪,再枚举另一头猪,两点确定一条过 (0, 0) 的抛物线,然后转移到当前集合与抛物线经过猪 的集合的并集。注意到每条抛物线经过猪的集合是可以预处理的。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;

int read() {
    int x = 0, f = 1; char c = getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
    return x * f;
}

#define maxn 23
#define maxs 362154
const double eps = 1e-6;
struct Point {
    double x, y;
    Point() {}
    Point(double _, double __): x(_), y(__) {}
    bool operator < (const Point& t) const { return x != t.x ? x < t.x : y < t.y; }
} ps[maxn];
int f[maxs], ls[maxn][maxn];

bool on(double a, double b, Point p) {
    return fabs(a * p.x * p.x + b * p.x - p.y) <= eps;
}

void up(int& a, int b) {
    if(a < 0) a = b;
    else a = min(a, b);
    return ;
}

int main() {
	freopen("angrybirds.in", "r", stdin);
	freopen("angrybirds.out", "w", stdout);
    int T = read();
    while(T--) {
        int n = read(); read();
        for(int i = 0; i < n; i++) scanf("%lf%lf", &ps[i].x, &ps[i].y);
        sort(ps, ps + n);
        memset(f, -1, sizeof(f));
        memset(ls, 0, sizeof(ls));
        f[0] = 0;
        for(int i = 0; i < n; i++)
        	for(int j = 0; j < n; j++) if(i != j) {
                double x1 = ps[i].x, y1 = ps[i].y, x2 = ps[j].x, y2 = ps[j].y;
                double b = (x1 * x1 * y2 - x2 * x2 * y1) / (x1 * x1 * x2 - x2 * x2 * x1);
                double a = (y1 - b * x1) / (x1 * x1);
                if(a >= 0.0) continue;
                int S = 0;
                for(int k = 0; k < n; k++) if(((S >> k & 1) ^ 1) && on(a, b, ps[k]))
                    S |= (1 << k);
        		ls[i][j] = S;
        	}
        int all = (1 << n) - 1;
        for(int S = 0; S <= all; S++) if(f[S] >= 0)
            for(int j = 0; j < n; j++) if((S >> j & 1) ^ 1) {
                int tS = S | (1 << j);
                up(f[tS], f[S] + 1);
                for(int i = j + 1; i < n; i++) if((tS >> i & 1) ^ 1)
                    up(f[tS | ls[i][j]], f[S] + 1);
                break;
            }
        printf("%d\n", f[all]);
    }
    
    return 0;
}

这次 NOIP 为什么都是第二题最难 TAT

NOIP2016题目整合