自嗨测试赛1

A Robert 的火车旅行

题目大意 : 每个点只有一条出边,一共可以走k条边,对于每个点回答有多少个点可以到达自己这个点

  • 不难发现图是一个内向基环树森林,环可以断环成链进行差分,除去环的部分就是树,树上差分就行

Code

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

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

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

bool v[N];
int n, k, to[N], stk[N], tp, a[N], cnt, b[N], w[N], s[N*2];

void Dfs(int x, int fa = 0) {
    v[x] = 1;
    stk[++tp] = x; w[x]++;
    if (tp >= k) w[stk[tp-k]]--;
    else s[b[stk[1]]+1]++, s[b[stk[1]]+min(k-tp+1, cnt)]--;
    for (int i = h[x], y; i; i = e[i].n)
        if (!b[y=e[i].t] && y != fa) Dfs(y, x), w[x] += w[y];
    tp--;
}

int main() {
    freopen("robert.in", "r", stdin);
    freopen("robert.out", "w", stdout);
    n = read(); k = read() + 1;
    for (int i = 1; i <= n; ++i) 
        to[i] = read(), Add(to[i], i);
    for (int j = 1; j <= n; ++j) {
        if (v[j]) continue;
        for (int x = j; !v[a[1] = x]; x = to[x]) v[x] = 1;
        b[a[1]] = ++cnt;
        for (int x = to[a[1]]; x != a[1]; x = to[x])
            b[x] = ++cnt, a[cnt] = x;
        for (int i = 1; i <= cnt; ++i) Dfs(a[i]);
        for (int i = 1; i <= cnt * 2; ++i) s[i] += s[i-1];
        for (int i = 1; i <= cnt; ++i) {
            w[a[i]] += s[i] + s[i+cnt]; 
            s[i] = s[i+cnt] = 0;
            b[a[cnt]] = 0;
        }
        cnt = 0;
    }
    for (int i = 1; i <= n; ++i)
        printf("%d\n", w[i]);
    return 0;
}

B 钻石教练老姚的神仙LIS

题目大意 : 求最长不降子序列,求最多可以同时找出多少个最长不降子序列,求a1,an可以重复选是最多可以同时找出多少个最长不降子序列

  • 数据范围1e3,n2建边跑最大流即可,lis=1是记得特判

Code

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

using namespace std;
const int N = 1005;

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, f;
}e[N*N];
int h[N*2], edc, tmp[N*2];

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

int n, a[N], f[N], lis, q[N*2], dep[N*2], s, t;

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

int Dinic(int x, int lim) {
    if (x == t) return lim;
    int sum = 0;
    for (int i = h[x]; i && lim; i = e[i].n) {
        int y = e[i].t; h[x] = i;
        if (!e[i].f || dep[y] != dep[x] + 1) continue;
        int f = Dinic(y, min(lim, e[i].f));
        lim -= f; sum += f;
        e[i].f -= f; e[i^1].f += f;
    }
    if (!sum) dep[x] = 0;
    return sum;
}

void Solve(bool g) {
    memset(h + 1, 0, t * 4); edc = 1;
    int ans = 0; s = n * 2 + 1; t = s + 1;
    for (int i = 1; i <= n; ++i) {
        int x = 1;
        if (g && (i == 1 || i == n)) x = 1e3;
        Add(i, i + n, x);
        if (f[i] == 1) Add(s, i, x);
        if (f[i] == lis) Add(i + n, t, x);
        for (int j = 1; j < i; ++j)
            if (a[j] <= a[i] && f[j] + 1 == f[i])
                Add(j + n, i, 1);
    }
    memcpy(tmp + 1, h + 1, t * 4);
    while (Bfs()) ans += Dinic(s, 1e3);
    printf("%d\n", ans);
}

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

C 组合空间

题目大意 : 给m个子集,问有多少种方案可以组成全集

  • 高维前缀和,子集反演,longdie说是最简单的签到题,改的时候感觉是3个里面最难的。可能是数学太差了吧

Code

Show Code
#include <cstdio>

using namespace std;
const int N = 1 << 22 | 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, ans, f[N], p[N];

int main() {
    freopen("longdie.in", "r", stdin);
    freopen("longdie.out", "w", stdout);
    m = read(); n = read();
    for (int i = 1; i <= n; ++i) {
        int x = 0;
        for (int k = read(); k; --k)
            x |= 1 << read() - 1;
        f[x ^ (1 << m) - 1]++;
    }
    p[0] = 1;
    for (int i = 1; i <= n; ++i)
        p[i] = 2 * p[i-1] % M;
    for (int i = 1; i <= m; ++i)
        for (int j = 0; j < 1 << m; ++j)
            if (j & 1 << i - 1) (f[j^(1<<i-1)] += f[j]) %= M;
    for (int s = (1 << m) - 1; s >= 0; --s) {
        int cnt = 0, x = p[f[s]] - 1;
        for (int i = s; i; i -= i & -i) cnt++;
        if (cnt & 1) ans = (ans - x + M) % M;
        else ans = (ans + x) % M;
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2021-03-09 18:38  Shawk  阅读(42)  评论(0编辑  收藏  举报