省选测试5

省选测试5

A 序列

题目大意 : 写一个程序,支持区间加,区间取max,单点询问值和修改过的次数(数值改变才算修改)

  • 挺裸的吉司机线段树,考试时写假了

Code

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

using namespace std;
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;
}

int n, a[N];

struct Tree {
    ll m, s, ta; //m -> mininum, s -> second mininum, ta -> tag_add
    int na, nc; //na -> number of times to add, nc -> number of times to modifications
}t[N*4];

void Pushup(int rt) {
    if (t[ls].m == t[rs].m) t[rt].m = t[ls].m, t[rt].s = min(t[ls].s, t[rs].s);
    else if (t[ls].m < t[rs].m) t[rt].m = t[ls].m, t[rt].s = min(t[ls].s, t[rs].m);
    else t[rt].m = t[rs].m, t[rt].s = min(t[ls].m, t[rs].s);
}

void Build(int rt, int l, int r) {
    if (l == r) return t[rt] = (Tree) {a[l], (ll)1e15}, void();
    int mid = l + r >> 1;
    Build(ls, l, mid); Build(rs, mid+1, r);
    Pushup(rt);
}

void Update(int rt, ll w, int nc) {
    if (t[rt].m < w) t[rt].m = w, t[rt].nc += nc;
}

void Pushdown(int rt) {
    if (t[rt].na) {
        ll ta = t[rt].ta; int na = t[rt].na; t[rt].ta = t[rt].na = 0;
        t[ls].m += ta; t[ls].s += ta; t[ls].ta += ta; t[ls].na += na;
        t[rs].m += ta; t[rs].s += ta; t[rs].ta += ta; t[rs].na += na;
    }
    if (t[rt].nc) {
        ll m = t[rt].m; int nc = t[rt].nc; t[rt].nc = 0;
        if (t[ls].m < m) t[ls].m = m, t[ls].nc += nc;
        if (t[rs].m < m) t[rs].m = m, t[rs].nc += nc;
    }
}

void Add(int rt, int l, int r, int x, int y, int w) {
    if (x <= l && r <= y) return t[rt].m += w, t[rt].s += w, t[rt].ta += w, t[rt].na++, void();
    int mid = l + r >> 1; Pushdown(rt);
    if (x <= mid) Add(ls, l, mid, x, y, w);
    if (y > mid) Add(rs, mid+1, r, x, y, w);
    Pushup(rt);
}

void Change(int rt, int l, int r, int x, int y, int w) {
    if (w <= t[rt].m) return;
    if (x <= l && r <= y && w < t[rt].s) {
        if (t[rt].m < w) t[rt].m = w, t[rt].nc++;
        return;
    }
    int mid = l + r >> 1; Pushdown(rt);
    if (x <= mid) Change(ls, l, mid, x, y, w);
    if (y > mid) Change(rs, mid+1, r, x, y, w);
    Pushup(rt);
}

int Ask(int rt, int l, int r, int x) {
    if (l == r) return rt;
    int mid = l + r >> 1; Pushdown(rt);
    if (x <= mid) return Ask(ls, l, mid, x);
    else return Ask(rs, mid + 1, r, x);
}

int main() {
    n = read();
    for (int i = 1; i <= n; ++i) a[i] = read();
    Build(1, 1, n);
    for (int m = read(); m; --m) {
        char od; scanf(" %c", &od);
        if (od == 'A') {
            int l = read(), r = read(), w = read();
            if (w) Add(1, 1, n, l, r, w);
        }
        else if (od == 'M') {
            int l = read(), r = read();
            Change(1, 1, n, l, r, read());
        }
        else {
            int rt = Ask(1, 1, n, read());
            printf("%lld %d\n", t[rt].m, t[rt].na + t[rt].nc);
        }
    }
    return 0;
}

B 旅行计划

题目大意 :给定一个带权无向图,每次询问两点间路径权值和 mod K 的最小值,可重复经过点和边

  • 对于 K 为奇数的部分如果联通就直接大力 puts("0") 就好了,因为只要存在一条路径,来回一共走 K 次就一定可以把权值和 mod K 变成 0

  • 正解的话先算出这个联通块里所有边与 K 的 GCD g,当 k/g 是奇数的时候就来回走 k/g 次可以把答案变为 0

  • 如果 k/g 是偶数,两点间距离是偶数也一定可以构造出 0 的情况,证明一下,我们现在可以走任意边偶数次并回到原点,那么这部分的权值和就是 \(\sum _i 2x_id_i\),现在所有的边和 K 都除掉他们的GCD后,由扩展裴蜀定理可知 \(\sum _i x_id_i\equiv 1\mod K\) ,所以 \(\sum _i 2x_id_i\equiv 2\mod K\),这样的话就一定可以构造出mod k值为2的路径, 走这种路径 ((k-x)mod k)/2 次就可以构造出0

  • 如果联通块里有奇环的话也可以构造出 0,原因是从起点经过奇环再回到起点再走奇数长度到终点,这样长度就是奇数+偶数+奇数=偶数,可以把这个当作偶数路径,然后就套用上一条的结论就能构造出0了

  • 如果上面的情况都没有的话那最小的可以构造出值为 g 的答案

Code

Show Code
#include <cstdio>

using namespace std;
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, d;
}e[N*2];
int h[N], edc;

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

bool v[N];
int n, m, q, f[N], g[N], c[N]; 

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

int Gcd(int a, int b) {
    return b ? Gcd(b, a % b) : a;
}

void Dfs(int x, int col) {
    int f = Find(x); c[x] = col;
    for (int i = h[x]; i; i = e[i].n) {
        int y = e[i].t, d = e[i].d / g[f];
        if (!c[y]) Dfs(y, col ^ (d & 1));
        else if ((col ^ (d & 1)) != c[y]) v[f] = 1;
    }
}

int main() {
    n = read(); m = read(); q = read();
    for (int i = 1; i <= n; ++i) f[i] = i;
    while (m--) {
        int x = read(), y = read(), z = read();
        Add(x, y, z); Add(y, x, z);
        x = Find(x), y = Find(y);
        if (x == y) g[x] = Gcd(g[x], z);
        else f[y] = x, g[x] = Gcd(g[x], Gcd(g[y], z));
    }
    for (int i = 1; i <= n; ++i) 
        if (!c[i]) Dfs(i, 2);
    while (q--) {
        int x = read(), y = read(), k = read(); 
        int fx = Find(x), fy = Find(y), gcd = Gcd(k, g[fx]);
        if (fx != fy) puts("NIE");
        else if ((k / gcd) & 1 || c[x] == c[y] || v[fx]) puts("0");
        else printf("%d\n", gcd);
    }
    return 0;
}

C Hack

题目大意 : 一个带权有向图,删去一些边保证从0不能走到n-1,并且任意一条路径上不能有两条边被删

  • 如果没有说一条路径上只能删一条边的限制就可以直接最小流了,而有这个限制的话就给反向边建inf,然后给能dfs到的点跑个最小割就可以了(其实是没懂的)

Code

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

using namespace std;
typedef long long ll;
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;
}

struct Edge {
    int n, t; ll d;
}e[N*N>>1];
int h[N], edc = 1;

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

ll ans;
bool v[N];
int n, dep[N], q[N], l, r, tmp[N];

void Dfs(int x) {
    v[x] = 1;
    for (int i = h[x], y; i; i = e[i].n)
        if (!v[y=e[i].t] && e[i].d != 1e12) Dfs(y);
}

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

ll Dinic(int x, ll lim) {
    if (x == n) return lim;
    ll 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;
        ll f = Dinic(y, min(lim, e[i].d));
        sum += f; lim -= f;
        e[i].d -= f; e[i^1].d += f;
    }
    if (!sum) dep[x] = 0;
    return sum;
}

int main() {
    n = read();
    for (int m = read(); m; --m) {
        int x = read() + 1, y = read() + 1, z = read();
        Add(x, y, z); Add(y, x, 1e12);
    }
    Dfs(1);
    if (!v[n]) return puts("-1"), 0;
    memcpy(tmp + 1, h + 1, n * 4);
    while (Bfs()) ans += Dinic(1, 1e12);
    printf("%lld\n", ans >= 1e12 ? -1ll : ans);
    return 0;
}
上一篇:51Node ——自然数幂和模板&&拉格朗日插值


下一篇:两台linux下发送简单消息,文本或文件