9.2训练赛

A . P1082 [NOIP2012 提高组] 同余方程

exgcd模板

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;

ll exgcd(ll a, ll b, ll &x, ll &y) {
    ll t;
    return b ? t = exgcd(b, a % b, y, x), y -= a / b * x, t : (x = 1, y = 0, a);
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    ll a, b, x, y;
    cin >> a >> b;
    exgcd(a, b, x, y);
    x = (x % b + b) % b;
    cout << x;
}

B . P6216 回文匹配

manacher + kmp 复杂度\(O(n)\),计数部分要求两次前缀和,注意串长限制。kmp可以用hash代替,复杂度不变,manacher可以用hash二分代替,复杂度\(O(nlogn)\)

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
const ll N = 3e6 + 10;
ll n, m;
ll len[N * 2], f[N], ps[N];
char a[N], b[N], s[N * 2];
unsigned ans;

inline void manacher(ll n) {
    for (ll i = 1, mx = 0, id; i < n; ++i) {
        len[i] = mx > i ? min(len[id * 2 - i], mx - i) : 1;
        for (; i >= len[i] && s[i + len[i]] == s[i - len[i]]; ++len[i])
            ;
        if (i + len[i] > mx)
            mx = i + len[i], id = i;
        if (i & 1 && len[i] > m) {
            ll j = i + 1 >> 1, k = len[i] - 1;
            ll r = j + k / 2, rm = j + m / 2 - 1;
            ll l = r - (k + 2 - m), lm = l + (r - rm);
            ans += ps[r] - ps[rm] - (ps[lm] - ps[l]);
        }
    }
}

inline void kmp(char *p, ll m, char *t, ll n) {
    for (ll i = 2, j = 0; i <= m; ++i) {
        while (j && p[i] ^ p[j + 1])
            j = f[j];
        f[i] = p[i] ^ p[j + 1] ? j : ++j;
    }
    for (ll i = 1, j = 0; i <= n; ++i) {
        while (j && t[i] ^ p[j + 1])
            j = f[j];
        j += t[i] == p[j + 1];
        ps[i] = ps[i - 1] + (j == m);
    }
    fu(i, 1, n) ps[i] += ps[i - 1];
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m >> a + 1 >> b + 1;
    kmp(b, m, a, n);
    fu(i, 1, n) s[i * 2 - 1] = a[i];
    manacher(n * 2);
    cout << ans;
}

C . P3180 [HAOI2016]地图

这是一个仙人掌图(任意一条边至多存在于一个简单环中),考虑dfs树,对于每个环,删除其中所有边并从dfs序最小的点向其它的点连边,问题便转换成了子树查询,可以用tarjan改变dfs序转为区间查询然后莫队 + 分块维护,也可以直接线段树合并。

#include <bits/stdc++.h>
#define fu(a, b, c) for (int a = b; a <= c; a++)
#define fd(a, b, c) for (int a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 1, M = 1e6 + 1, B = 320, B1 = 1e3;
int n, m, no;
int x, l, r, u, v, d, ans[N];
int w[N], W[N], sz[N], id[N];
int dfn[N], low[N], dx[N];
int cnt[M], bl[B1 + 1][2];
vector<int> g[N];

struct Q {
    int l, r, y, o, i, bi;
    bool operator<(const Q &a) {
        return bi ^ a.bi ? bi < a.bi : (bi & 1 ? r < a.r : r > a.r);
    }
} q[N];

inline void tarjan(int x, int f) {
    dfn[x] = low[x] = ++d, dx[d] = x;
    for (int v : g[x])
        if (v ^ f) {
            if (!dfn[v])
                tarjan(v, x), mn(low[x], low[v]);
            else
                mn(low[x], dfn[v]);
        }
}

void dfs(int u) {
    id[u] = ++no, W[no] = w[u], ++sz[u];
    for (int v : g[u])
        if (!id[v] && low[v] >= dfn[u])
            dfs(v), sz[u] += sz[v];
    for (int v : g[u])
        if (!id[v])
            dfs(v), sz[dx[low[v]]] += sz[v];
}

inline void del(int x) {
    int bi = W[x] / B1, f = cnt[W[x]] & 1;
    if (cnt[W[x]] > 0)
        --bl[bi][f];
    --cnt[W[x]];
    if (cnt[W[x]] > 0)
        ++bl[bi][!f];
}

inline void add(int x) {
    int bi = W[x] / B1, f = cnt[W[x]] & 1;
    if (cnt[W[x]] > 0)
        --bl[bi][f];
    ++cnt[W[x]];
    if (cnt[W[x]] > 0)
        ++bl[bi][!f];
}

inline int query(int y, int o) {
    int r = (y + 1) / B1, res = 0;
    fu(i, 0, r - 1) res += bl[i][o];
    fu(i, r * B1, y) res += cnt[i] > 0 && (cnt[i] & 1) == o;
    return res;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m;
    fu(i, 1, n) cin >> w[i];
    fu(i, 1, m) {
        cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    tarjan(1, 1);
    dfs(1);
    cin >> m;
    fu(i, 1, m) {
        cin >> q[i].o >> x >> q[i].y;
        q[i].l = id[x], q[i].r = id[x] + sz[x] - 1;
        q[i].i = i, q[i].bi = id[x] / B;
    }
    sort(q + 1, q + m + 1);
    fu(i, 1, m) {
        while (l < q[i].l)
            del(l++);
        while (l > q[i].l)
            add(--l);
        while (r > q[i].r)
            del(r--);
        while (r < q[i].r)
            add(++r);
        ans[q[i].i] = query(q[i].y, q[i].o);
    }
    fu(i, 1, m) cout << ans[i] << '\n';
}

D . P2599 [ZJOI2009]取石子游戏

\(l[i][j]\)代表在区间\([i,j]\)左侧加上石子后达到必败态所需数量,由博弈论知识可知\(l[i][j]\)存在且唯一,\(r[i][j]\)同理,那么我们只要判断\(l[2][n] == a[1]\ ?\)。接下来进行讨论,设\(l = l[i][j - 1], r = r[i][j - 1], x = a[j]\)

  1. \(x = r\),令\(l[i][j] = 0\),已是必败态。
  2. \(x < l, x < r\),令\(l[i][j] = x\),无论先手从哪边拿石子,后手都从另一边拿相同的石子,那么必然是先手先拿完某一边的石子,后手必胜。
  3. \(r < x <= l\),令\(l[i][j] = x - 1\),如果先手拿左边,左边剩余石子\(y\),如果\(y >= r\),后手把右边拿成\(y + 1\),回到case3。如果\(y < r\),后手把右边拿成\(y\),回到case2。如果先手拿右边剩余石子\(y\),如果\(y = r\),后手取完左边,如果\(y < r\),后手把左边取到\(y\),回到case2,如果\(y > r\),后手把左边取到\(y - 1\),回到case3,以下分析类似。
  4. \(l <= x < r\),令\(l[i][j] = x + 1\)
  5. \(x > l, x > r\),令\(l[i][j] = x\)
#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
const ll N = 1e3 + 1;
ll t, n, m, no;
ll a[N], l[N][N], r[N][N];

ll funr(ll i, ll j);

ll funl(ll i, ll j) {
    if (~l[i][j])
        return l[i][j];
    ll L = funl(i, j - 1), R = funr(i, j - 1);
    if (a[j] == R)
        return l[i][j] = 0;
    else if (a[j] > L & a[j] > R | a[j] < L & a[j] < R)
        return l[i][j] = a[j];
    else
        return l[i][j] = a[j] + (a[j] > R ? -1 : 1);
}

ll funr(ll i, ll j) {
    if (~r[i][j])
        return r[i][j];
    ll L = funl(i + 1, j), R = funr(i + 1, j);
    if (a[i] == L)
        return r[i][j] = 0;
    else if (a[i] > L & a[i] > R | a[i] < L & a[i] < R)
        return r[i][j] = a[i];
    else
        return r[i][j] = a[i] + (a[i] > L ? -1 : 1);
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> t;
    while (t--) {
        cin >> n;
        memset(l, -1, sizeof l), memset(r, -1, sizeof r);
        fu(i, 1, n) cin >> a[i], l[i][i] = r[i][i] = a[i];
        puts(funl(2, n) == a[1] ? "0" : "1");
    }
}

E . P1503 鬼子进村

用set和stack维护被摧毁和修复的房子即可

#include <bits/stdc++.h>
#define fu(a, b, c) for (ll a = b; a <= c; a++)
#define fd(a, b, c) for (ll a = b; a >= c; a--)
#define mx(a, b) a = max(a, b)
#define mn(a, b) a = min(a, b)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll N = 1e5 + 1, M = 1e2 + 1;
ll t, n, m, k;
ll a, b, c, d, e, f;
ll l, r, ans;
ll st[N];
set<ll> s;

int main() {
    ios::sync_with_stdio(0), cin.tie(0);
    cin >> n >> m;
    s.emplace(0), s.emplace(n + 1);
    char ch;
    fu(i, 1, m) {
        cin >> ch;
        if (ch == 'R')
            s.erase(st[--c]);
        else {
            cin >> b;
            if (ch == 'D')
                st[c++] = b, s.emplace(b);
            else {
                auto i = s.lower_bound(b);
                cout << (*i == b ? 0 : *i - *--i - 1) << '\n';
            }
        }
    }
}

F . P1058 [NOIP2008 普及组] 立体图

写一个画一个立方体的函数,从后往前,从左往右,从下往上画,记录一下边界,输出即可。

以前的代码

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int m, n, a[50][50], h, w;
char pic[401][301];
char s0[6] = "+---+", s1[7] = "|   |/", s2[8] = "|   | +", s3[8] = "+---+ |", s4[7] = "/   /|", s5[6] = "+---+";

void draw(int i, int j) { //以i,j为左下角画立方体
    memcpy(&(pic[i][j]), s0, 5);
    memcpy(&(pic[i - 1][j]), s1, 6);
    memcpy(&(pic[i - 2][j]), s2, 7);
    memcpy(&(pic[i - 3][j]), s3, 7);
    memcpy(&(pic[i - 4][j + 1]), s4, 6);
    memcpy(&(pic[i - 5][j + 2]), s5, 5);
}

int main() {
    scanf("%d %d", &m, &n);
    w = 4 * n + 1 + 2 * m;
    for (int i = 0; i < m; i++) {     //i行数
        for (int j = 0; j < n; j++) { //j列数
            scanf("%d", &a[i][j]);
            h = max(3 * a[i][j] + 1 + 2 * (m - i), h);
        }
    }
    memset(pic, '.', sizeof(pic));
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            for (int k = 1; k <= a[i][j]; k++) { //i,j对应画布h-2*(m-i-1),2*(m-i-1)+4*j
                draw(h - 2 * (m - i - 1) - (k - 1) * 3 - 1, 2 * (m - i - 1) + 4 * j);
            }
        }
    }
    for (int i = 0; i < h; i++) {
        for (int j = 0; j < w; j++) {
            printf("%c", pic[i][j]);
        }
        printf("\n");
    }
    return 0;
}
posted @ 2021-09-04 21:52  lemu  阅读(32)  评论(0编辑  收藏  举报
4