首页 > 其他分享 >POI-4

POI-4

时间:2024-04-19 22:23:13浏览次数:12  
标签:cnt return int 1000005 ecnt POI include

T1

洛谷 P3479 GAS-Fire Extinguishers

考虑贪心,从叶子往上,对于每个点能不放就不放,实在要放了再放。要知道一个点能不能不放,需要知道子树中最深的还没有被覆盖的点的距离。记 \(f[i][j]\) 为 \(i\) 子树中距离 \(i\) 为 \(j\) 的点有多少点没有被覆盖。一个点放了灭火器之后可以覆盖子树之外的点,所以还要记录 \(g[i][j]\) 表示 \(i\) 子树中距离 \(i\) 为 \(j\) 的点上的灭火器总共还能覆盖多少点。到每个点,先转移 \(f,g\),然后如果 \(f[i][k] \ne 0\) 则在 \(i\) 放 \(\lceil \frac{f[i][k]}{s} \rceil\) 个灭火器。然后如果两个子树中分别有距离 \(i\) 为 \(j\) 和 \(k - j\) 或 \(k - j - 1\) 的未覆盖点和灭火器,那么用灭火器把这个未覆盖点消掉。最后在根尽量用灭火器把所有剩下的点都消掉,然后再看还有多少点需要覆盖。

代码
#include <iostream>
#define int long long
using namespace std;
int n, s, d;
int head[100005], nxt[200005], to[200005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int f[100005][21];
int g[100005][21];
int ans = 0;
void dfs(int x, int fa) {
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            dfs(v, x);
            for (int i = 1; i <= d; i++) {
                f[x][i] += f[v][i - 1];
                g[x][i] += g[v][i - 1];
            }
        }
    }
    ++g[x][0];
    int tmp = (g[x][d] + s - 1) / s;
    f[x][0] += tmp * s;
    ans += tmp;
    for (int i = 0; i <= d; i++) {
        int j = d - i;
        int t = min(f[x][i], g[x][j]);
        f[x][i] -= t, g[x][j] -= t;
    }
    for (int i = 0; i < d; i++) {
        int j = d - 1 - i;
        int t = min(f[x][i], g[x][j]);
        f[x][i] -= t, g[x][j] -= t;
    }
}
signed main() {
    cin >> n >> s >> d;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    dfs(1, 0);
    for (int i = 0; i <= d; i++) {
        for (int j = d - i; ~j; j--) {
            int tmp = min(f[1][i], g[1][j]);
            f[1][i] -= tmp, g[1][j] -= tmp;
        }
    }
    int asdf = 0;
    for (int i = 0; i <= d; i++) asdf += g[1][i];
    ans += (asdf + s - 1) / s;
    cout << ans << "\n";
    return 0;
}

T2

洛谷 P3480 KAM-Pebbles

差分,然后就变成阶梯博弈了。只不过是每次把左边的扔到右边。

代码
#include <iostream>
using namespace std;
int a[1005];
int main() {
    int tc;
    cin >> tc;
    while (tc--) {
        int n;
        cin >> n;
        int ans = 0;
        for (int i = 1; i <= n; i++) cin >> a[i], ans ^= (((n - i + 1) & 1) * (a[i] - a[i - 1]));
        cout << (ans ? "TAK\n" : "NIE\n");
    }
    return 0;
}

T4

洛谷 P3482 SLO-Elephants

首先肯定是若干个环。在环之内拆掉这个环的最优方案就是把代价最小的数每次和它所指向的点交换位置,这样解决一个环的代价就是 \(sum + mn \times (l - 2)\),其中 \(sum\) 是环内代价和,\(mn\) 是最小代价,\(l\) 是环长。然后发现如果一个环内所有数都很大,我们可以把全局的最小值和环内最小值先换位,然后拿全局最小值把这个环搞掉,然后再换回来。所以对于每个环都求一下这两种代价然后取最小值加起来即可。

代码
#include <iostream>
#define int long long
using namespace std;
int n;
int val[1000005];
int f[1000005], s[1000005], v[1000005], sz[1000005], a[1000005], b[1000005];
int m = 2147483647;
int getf(int x) { return (f[x] == x ? x : (f[x] = getf(f[x]))); }
bool Merge(int x, int y) {
    x = getf(x), y = getf(y);
    if (x == y) 
        return 0;
    if (sz[x] > sz[y]) 
        swap(x, y);
    f[x] = y;
    s[y] += s[x];
    sz[y] += sz[x];
    v[y] = min(v[y], v[x]);
    return 1;
}
int ans = 0;
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> val[i], f[i] = i, sz[i] = 1, s[i] = v[i] = val[i], m = min(m, v[i]);
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        if (!Merge(a[i], b[i])) {
            int x = getf(a[i]);
            ans += (sz[x] != 1) * min(s[x] + v[x] * (sz[x] - 2), s[x] + v[x] + m * (sz[x] + 1));
        }
    }
    cout << ans << "\n";
    return 0;
}

T5

洛谷 P3485 BAJ-The Walk of the Bytie-boy

考虑 dp,朴素的就是 \(f[i][j]\) 表示 \(i \rightarrow j\) 是回文的最小步数,每次枚举两条相等的边转移。但是这样是 \(m^2\) 的,爆炸。考虑把 dp 状态拆掉。令 \(f[i][j]\) 还是不变,\(g[i][j][c]\) 表示 \(i \rightarrow k \rightarrow j\),其中前一段是回文,后一段只有一条边权为 \(c\) 的边的最小步数。转移是简单的,使用 bfs 即可。注意要开两个 bfs 队列分别放两种 dp 值。

代码
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
using namespace std;
int n, m;
vector<int> vec1[405][30];
vector<int> vec2[405][30];
struct node1 {
    int x, y;
} pg[405][405][30];
struct node2 {
    int x, y, c;
} pf[405][405];
queue<node1> q1;
queue<node2> q2;
int f[405][405], g[405][405][30];
void bfs() {
    while (!q1.empty() || !q2.empty()) {
        if (!q1.empty() && (q2.empty() || f[q1.front().x][q1.front().y] <= g[q2.front().x][q2.front().y][q2.front().c])) {
            node1 tmp = q1.front();
            q1.pop();
            int x = tmp.x, y = tmp.y;
            for (int i = 0; i < 26; i++) {
                for (auto v : vec1[y][i]) {
                    if (g[x][v][i] > f[x][y] + 1) {
                        g[x][v][i] = f[x][y] + 1;
                        pg[x][v][i] = (node1) { x, y };
                        q2.push((node2) { x, v, i });
                    }
                }
            }
        } else {
            node2 tmp = q2.front();
            q2.pop();
            int x = tmp.x, y = tmp.y, z = tmp.c;
            for (auto v : vec2[x][z]) {
                if (f[v][y] > g[x][y][z] + 1) {
                    f[v][y] = g[x][y][z] + 1;
                    pf[v][y] = (node2) { x, y, z };
                    q1.push((node1) { v, y });
                }
            }
        }
    }
}
int ans[100005];
void print(int x, int y) {
    int l = 0, r = f[x][y] + 1, t = f[x][y];
    while (l + 1 < r) {
        node2 tmp1 = pf[x][y];
        ans[++l] = tmp1.c;
        x = tmp1.x, y = tmp1.y;
        if (l + 1 >= r) 
            break;
        node1 tmp2 = pg[x][y][tmp1.c];
        ans[--r] = tmp1.c;
        x = tmp2.x, y = tmp2.y;
    }
    for (int i = 1; i <= t; i++) cout << (char)(ans[i] + 'a');
    cout << "\n";
}
int main() {
    memset(f, 63, sizeof f);
    memset(g, 63, sizeof g);
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        char c;
        cin >> c;
        vec1[u][c - 'a'].emplace_back(v);
        vec2[v][c - 'a'].emplace_back(u);
        f[u][v] = 1;
        pf[u][v] = (node2) { 0, 0, c - 'a' };
        q1.push((node1) { u, v });
    }
    for (int i = 1; i <= n; i++) f[i][i] = 0, q1.push((node1) { i, i });
    bfs();
    int d, x, y;
    cin >> d;
    cin >> x;
    --d;
    while (d--) {
        cin >> y;
        if (f[x][y] != 0x3f3f3f3f) 
            cout << f[x][y] << " ", print(x, y);
        else 
            cout << -1 << "\n";
        x = y;
    }
    return 0;
}

T6

洛谷 P3488 LYZ-Ice Skates

类似于二分图完美匹配的东西,考虑 Hall 定理:对于左部点的任意子集,其在右部点的邻集大小都大于等于该子集。我们考虑这个条件最有希望不满足的时候肯定是左边选一段尺码连续的人,这样邻集才能尽量小。所以只需要对每个区间看 \(\sum r_i\) 是否小于等于 \(k(r + d - l + 1)\) 即可。稍微移个项,发现是个最大子段和,直接线段树即可。

代码
#include <iostream>
#define int long long
using namespace std;
int n, m, k, d;
struct node {
    int s, mx, lmx, rmx;
} T[1000005];
node operator+(node a, node b) {
    node c;
    c.s = a.s + b.s;
    c.mx = max(a.rmx + b.lmx, max(a.mx, b.mx));
    c.lmx = max(a.lmx, a.s + b.lmx);
    c.rmx = max(b.rmx, b.s + a.rmx);
    return c;
}
struct Segment_Tree {
    void Build(int o, int l, int r) {
        T[o].mx = T[o].lmx = T[o].rmx = -k;
        T[o].s = -k * (r - l + 1);
        if (l == r) 
            return;
        int mid = (l + r) >> 1;
        Build(o << 1, l, mid);
        Build(o << 1 | 1, mid + 1, r);
    }
    void Add(int o, int l, int r, int x, int y) {
        if (l == r) {
            T[o].mx += y;
            T[o].lmx += y;
            T[o].rmx += y;
            T[o].s += y;
            return;
        }
        int mid = (l + r) >> 1;
        if (x <= mid) 
            Add(o << 1, l, mid, x, y);
        else 
            Add(o << 1 | 1, mid + 1, r, x, y);
        T[o] = T[o << 1] + T[o << 1 | 1];
    }
    node Query(int o, int l, int r, int L, int R) {
        if (L <= l && r <= R) 
            return T[o];
        int mid = (l + r) >> 1;
        if (R <= mid) 
            return Query(o << 1, l, mid, L, R);
        if (L > mid) 
            return Query(o << 1 | 1, mid + 1, r, L, R);
        return Query(o << 1, l, mid, L, R) + Query(o << 1 | 1, mid + 1, r, L, R);
    }
} seg;
signed main() {
    cin >> n >> m >> k >> d;
    seg.Build(1, 1, n);
    while (m--) {
        int x, y;
        cin >> x >> y;
        seg.Add(1, 1, n, x, y);
        cout << (seg.Query(1, 1, n, 1, n).mx <= k * d ? "TAK\n" : "NIE\n");
    }
    return 0;
}

T7

洛谷 P3497 KOL-Railway

首先观察到如果存在 \(i < j < k\) 使得 \(a_k < a_j < a_i\),那么 \(i\) 和 \(j\) 不能在同一个栈中。因为在 \(k\) 加入之前,\(i, j\) 都不会被弹出,而如果在同一个栈中的话,栈中就会出现顺序对,而这是不合法的。所以倒序枚举,维护后缀最小值 \(mn\),每个点和尚未枚举到的、权值在 \((mn, a_i)\) 的点连边表示不在同一个栈中。暴力连边显然是 \(n^2\) 的,考虑线段树优化建图。由于是双向边,需要分别维护入树与出树。考虑如何只连没遍历到的点,我们可以把所有遍历到的点到根的路径上所有点都换一个新的,然后新的点就不向原来的叶子连边。这样以后连边都不会走到这个叶子。接下来 dfs 染色,原图中能够到达的两个叶子不能同色。但是对于线段树上的辅助节点,其颜色不能乱设。考虑第一次遍历到这个点时最后一个访问的叶子,很显然这个点能够到达的所有叶子都和最后一个访问的叶子异色。如果之后再来到这个点,但是最后一个访问的叶子颜色改变了,这样就会在叶子上产生矛盾。所以线段树上节点的颜色第一次访问到这个点时最后一个访问的叶子的颜色。这样所有点的染色方案都确定了。至于字典序最小,只需要顺序遍历每个点,如果没被染色就染 \(1\),然后确定它能染的所有点。这样就能保证字典序最小。

代码
#include <iostream>
using namespace std;
int n;
int a[100005];
bool lf[6800005];
int head[6800005], nxt[13600005], to[13600005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
struct node {
    int l, r, in, out;
} T[1000005];
int ncnt;
struct Segment_Tree {
    void Build(int o, int l, int r) {
        T[o].in = ++ncnt, T[o].out = ++ncnt;
        if (l == r) {
            T[o].in = T[o].out = l;
            return;
        }
        int mid = (l + r) >> 1;
        Build(o << 1, l, mid);
        Build(o << 1 | 1, mid + 1, r);
        add(T[o].in, T[o << 1].in), add(T[o].in, T[o << 1 | 1].in);
        add(T[o << 1].out, T[o].out), add(T[o << 1 | 1].out, T[o].out);
    }
    void Add(int o, int l, int r, int L, int R, int x) {
        if (L <= l && r <= R) {
            if (l != r || !lf[l]) {
                add(x, T[o].in);
                add(T[o].out, x);
            }
            return;
        }
        int mid = (l + r) >> 1;
        if (L <= mid) 
            Add(o << 1, l, mid, L, R, x);
        if (R > mid) 
            Add(o << 1 | 1, mid + 1, r, L, R, x);
    }
    void Work(int o, int l, int r, int x) {
        if (l == r) 
            return;
        int mid = (l + r) >> 1;
        if (x <= mid) 
            Work(o << 1, l, mid, x);
        else 
            Work(o << 1 | 1, mid + 1, r, x);
        T[o].in = ++ncnt, T[o].out = ++ncnt;
        if (l != mid || !lf[l]) 
            add(T[o].in, T[o << 1].in), add(T[o << 1].out, T[o].out);
        if (mid + 1 != r || !lf[r]) 
            add(T[o].in, T[o << 1 | 1].in), add(T[o << 1 | 1].out, T[o].out);
    }
} seg;
bool vis[6800005];
int clr[6800005];
void dfs(int x, int lst) {
    vis[x] = 1;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v <= n) {
            if (!vis[v]) {
                clr[v] = clr[x] ^ 1;
                dfs(v, v);
            } else if (v != lst && clr[v] == clr[x]) {
                cout << "NIE\n";
                exit(0);
            }
        } else {
            if (!vis[v]) {
                clr[v] = clr[x];
                dfs(v, lst);
            } else if (lf[v] && clr[v] != clr[x]) {
                cout << "NIE\n";
                exit(0);
            }
        }
        lf[x] |= lf[v];
    }
}
int main() {
    // freopen("data.in", "r", stdin);
    // freopen("data.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n;
    ncnt = n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    seg.Build(1, 1, n);
    for (int i = n, m = n + 1; i; i--) {
        m = min(m, a[i]);
        if (m < a[i] - 1) 
            seg.Add(1, 1, n, m + 1, a[i] - 1, a[i]);
        lf[a[i]] = 1;
        seg.Work(1, 1, n, a[i]);
    }
    for (int i = 1; i <= n; i++) {
        if (!vis[a[i]]) 
            dfs(a[i], a[i]);
    }
    cout << "TAK\n";
    for (int i = 1; i <= n; i++) cout << clr[a[i]] + 1 << " ";
    cout << "\n";
}

T8

洛谷 P3499 NAJ-Divine Divisor

首先拿 \(10^6\) 以内的所有素数把所有数除一遍,然后考虑得到的数,只有以下四种情况:

  1. \(a[i] = 1\)

  2. \(a[i] = p\)

  3. \(a[i] = pq\)

  4. \(a[i] = p^2\)

其中 \(p\) 和 \(q\) 都是质数。第一类不管,第二类使用 Miller-Rabin 判一下,第四类就看是不是完全平方数,第三类考虑与别的所有数求 \(\gcd\),求出来要是不为 \(1\),说明是拆开了,否则说明这个数里的质数没有在别的数里出现过,所以我们并不关心质数的具体数值,只需要统计下出现次数即可。

代码
#include <iostream>
#include <string.h>
#include <math.h>
#include <map>
#define int long long
#define i128 __int128
using namespace std;
int n;
map<int, int> mp;
map<int, int> bonus;
int p[1000005], pcnt;
bool mark[1000005];
void Sieve(int n) {
    for (int i = 2; i <= n; i++) {
        if (!mark[i]) 
            p[++pcnt] = i;
        for (int j = 1; p[j] * i <= n && j <= pcnt; j++) {
            mark[p[j] * i] = 1;
            if (i % p[j] == 0) 
                break;
        }
    }
}
int gcd(int a, int b) { return (b ? gcd(b, a % b) : a); }
int mr[12] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 };
inline int qpow(int x, int y, int P) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = (i128)ret * x % P;
        y >>= 1;
        x = (i128)x * x % P;
    }
    return ret;
}
bool check(int a, int p) {
    int d = p - 1;
    if (qpow(a, d, p) != 1) 
        return 1;
    int tmp;
    while (!(d & 1)) {
        tmp = qpow(a, d >>= 1, p);
        if (tmp == p - 1) 
            return 0;
        else if (tmp != 1) 
            return 1;
    }
    return 0;
}
bool isPrime(int x) {
    for (int i = 0; i < 12; i++) {
        if (x == mr[i]) 
            return 1;
    }
    for (int i = 0; i < 12; i++) {
        if (check(mr[i], x)) 
            return 0;
    }
    return 1;
}
int cnt[1000005];
int a[1000005];
int cnt1;
char ans[1000005];
signed main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    Sieve(1000000);
    for (int i = 1; i <= pcnt; i++) {
        for (int j = 1; j <= n; j++) 
            while (a[j] % p[i] == 0) ++cnt[i], a[j] /= p[i];
    }
    // 1, p, p ^ 2, p * q
    for (int i = 1; i <= n; i++) {
        if (a[i] == 1) 
            continue;
        if (isPrime(a[i])) {
            ++mp[a[i]];
            continue;
        }
        int r = sqrt(a[i]);
        if (r * r == a[i]) {
            mp[r] += 2;
            continue;
        }
        bool rec = 0;
        for (int j = 1; j <= n; j++) {
            if (a[i] == a[j]) 
                continue;
            int tmp = gcd(a[i], a[j]);
            if (tmp != 1) {
                mp[tmp]++, mp[a[i] / tmp]++;
                rec = 1;
                break;
            }
        }
        if (!rec) 
            bonus[a[i]]++;
    }
    int mxc = 0, mcnt = 0;
    for (int i = 1; i <= pcnt; i++) {
        if (cnt[i] > mxc) {
            mxc = cnt[i];
            mcnt = 0;
        }
        mcnt += (cnt[i] == mxc);
    }
    for (auto v : mp) {
        if (v.second > mxc) {
            mxc = v.second;
            mcnt = 0;
        }
        mcnt += (v.second == mxc);
    }
    for (auto v : bonus) {
        if (v.second > mxc) {
            mxc = v.second;
            mcnt = 0;
        }
        mcnt += (v.second == mxc) * 2;
    }
    cout << mxc << "\n";
    sprintf(ans, "%.0Lf", powl(2.0L, mcnt));
    ans[strlen(ans) - 1]--;
    double x = 1;
    while (mcnt--) x *= 2;
    cout << ans << "\n";
    return 0;
}

T9

洛谷 P3502 CHO-Hamsters

考虑 \(dp[i][j]\) 表示总共出现了 \(i\) 次,最后一个串是 \(j\) 的最小代价。考虑往串后面接一个 \(k\) 串,则肯定是把 \(k\) 中与 \(j\) 后缀重复的最长部分删掉,然后把剩下的接上去。这样两个串之间转移的代价就可以算了,直接 \(\min +\) 广义矩乘优化 dp 即可。

代码
#include <iostream>
#define int long long
using namespace std;
const int inf = 0x7fffffffffffff;
int n, m;
struct Matrix {
    int a[200][200];
    int* operator[](int x) { return a[x]; }
} I, E, T;
Matrix operator*(Matrix a, Matrix b) {
    Matrix c;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            c[i][j] = inf;
            for (int k = 0; k < n; k++) 
                c[i][j] = min(c[i][j], a[i][k] + b[k][j]);
        }
    }
    return c;
}
Matrix qpow(Matrix x, int y) {
    Matrix ret = E;
    while (y) {
        if (y & 1) 
            ret = ret * x;
        y >>= 1;
        x = x * x;
    }
    return ret;
}
string str[205];
int nxt[100005];
int calc(string s) {
    nxt[0] = -1, nxt[1] = 0;
    int n = s.size();
    s = ' ' + s;
    for (int i = 2, j = 0; i <= n; i++) {
        while (j != -1 && s[j + 1] != s[i]) j = nxt[j];
        nxt[i] = ++j;
    }
    return nxt[n];
}
signed main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> str[i];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            E[i - 1][j - 1] = inf;
            if (i == j) 
                T[i - 1][j - 1] = str[j].size() - calc(str[i]);
            else 
                T[i - 1][j - 1] = str[j].size() - calc(str[j] + str[i]);
        }
        E[i - 1][i - 1] = 0;
    }
    for (int i = 1; i <= n; i++) I[0][i - 1] = str[i].size();
    I = I * qpow(T, m - 1);
    int ans = inf;
    for (int i = 1; i <= n; i++) ans = min(ans, I[0][i - 1]);
    cout << ans << "\n";
    return 0;
}

T10

洛谷 P3505 TEL-Teleportation

把图分层,第一层是 \(1\),第二层是与 \(1\) 直接相连的所有点,第三层是与第二层直接相连的所有点(不含 \(1\))。第六层是 \(2\),第五层是与 \(2\) 直接相连的所有点,第四层是与第五层直接相连的所有点(不含 \(2\))。剩下的点先不管。这样的一张图,首先层之内可以任意连边,然后相邻的两层之间任两点都能互相连边。将所有边都连上,最后减去初始的边就能算出最多新建的边数。至于剩下的点,我们把这些点分配到第三层时的答案算一下和分配到第四层答案算出来取最大值即为最后的答案。证明考虑把两个代价分别列出来,设分配 \(x\) 个点到第三层的答案为 \(f(x)\),然后能够发现是一个一次函数,所以最值就在两个端点处取到。

代码
#include <iostream>
#include <queue>
#define int long long
using namespace std;
int n, m;
int head[40005], nxt[2000005], to[2000005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int cnt[10];
int v[40005];
queue<int> q;
void bfs(int S) {
    q.push(S);
    if (S == 1) 
        v[S] = 1;
    else 
        v[S] = 6;
    while (!q.empty()) {
        int x = q.front();
        q.pop();
        if (v[x] == 3 || v[x] == 4) 
            continue;
        for (int i = head[x]; i; i = nxt[i]) {
            int vv = to[i];
            if (!v[vv]) {
                v[vv] = v[x] + (S == 1 ? 1 : -1);
                q.push(vv);
            }
        }
    }
}
int f(int x) { return x * (x - 1) / 2; }
int calc() {
    return cnt[1] * cnt[2] + cnt[2] * cnt[3] + cnt[3] * cnt[4] + cnt[4] * cnt[5] + cnt[5] * cnt[6] + f(cnt[1]) + f(cnt[2]) + f(cnt[3]) + f(cnt[4]) + f(cnt[5]) + f(cnt[6]);
}
signed main() {
    cin >> n >> m;
    for (int i = 1, u, v; i <= m; i++) {
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    bfs(1), bfs(2);
    for (int i = 1; i <= n; i++) cnt[v[i]]++;
    int ans = 0;
    cnt[3] += cnt[0], ans = calc();
    cnt[3] -= cnt[0], cnt[4] += cnt[0], ans = max(ans, calc());
    cout << ans - m << "\n";
    return 0;
}

T11

洛谷 P3506 MOT-Monotonicity

直接设 \(f_i\) 表示以 \(i\) 结尾的最大长度,然后根据长度确定接下来的大小关系,使用树状数组优化转移。这个 dp 虽然看起来不像对的,但是它确实具有最优子结构,它确实是对的。

代码
#include <iostream>
#define lowbit(x) ((x) &(-(x)))
using namespace std;
inline int read() {
    int ret = 0;
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    while ('0' <= c && c <= '9') ret = ret * 10 + c - 48, c = getchar();
    return ret;
}
int n, k;
string str, s;
int a[500005];
struct PBIT {
    int bit[1000005], p[1000005];
    void add(int x, int y, int pos) { for (; x <= 1000000; x += lowbit(x)) y >= bit[x] ? (p[x] = pos, bit[x] = y) : 0; }
    pair<int, int> query(int x) {
        int ret = 0, pos = 0;
        for (; x; x -= lowbit(x)) bit[x] > ret ? (ret = bit[x], pos = p[x]) : 0;
        return make_pair(ret, pos);
    }
} pbit;
struct SBIT {
    int bit[1000005], p[1000005];
    void add(int x, int y, int pos) { for (; x; x -= lowbit(x)) y >= bit[x] ? (p[x] = pos, bit[x] = y) : 0; }
    pair<int, int> query(int x) {
        int ret = 0, pos = 0;
        for (; x <= 1000000; x += lowbit(x)) bit[x] > ret ? (ret = bit[x], pos = p[x]) : 0;
        return make_pair(ret, pos);
    }
} sbit;
struct BIT {
    int bit[1000005], p[1000005];
    void add(int x, int y, int pos) { y > bit[x] ? (p[x] = pos, bit[x] = y) : 0; }
    pair<int, int> query(int x) { return make_pair(bit[x], p[x]); }
} bit;
pair<int, int> dp[5000005];
int ans[500005], acnt;
int main() {
    n = read(), k = read();
    for (int i = 1; i <= n; i++) a[i] = read();
    for (int i = 1; i <= k; i++) {
        char c = getchar();
        s += c;
        getchar();
    }
    while (str.size() < n) str += s;
    str = ' ' + str;
    int ans = 0, mp = 0;
    for (int i = 1; i <= n; i++) {
        pair<int, int> mx1 = pbit.query(a[i] - 1);
        pair<int, int> mx2 = sbit.query(a[i] + 1);
        pair<int, int> mx3 = bit.query(a[i]);
        pair<int, int> mx = max(mx1, max(mx2, mx3));
        dp[i] = mx;
        ++dp[i].first;
        if (dp[i].first > ans) 
            ans = dp[i].first, mp = i;
        if (str[dp[i].first] == '=') 
            bit.add(a[i], dp[i].first, i);
        if (str[dp[i].first] == '<') 
            pbit.add(a[i], dp[i].first, i);
        if (str[dp[i].first] == '>') 
            sbit.add(a[i], dp[i].first, i);
    }
    cout << dp[mp].first << "\n";
    return 0;
}

T12

洛谷 P3511 MOS-Bridges

首先考虑二分答案,然后有些边的某些方向就会被 ban 掉。对于剩下的图,我们要为每条方向未定的边确定一个方向,使得图中存在一个欧拉回路。关于欧拉回路,有一个结论:每个点的入度都等于出度等于度数的一半。有了这个结论,就可以通过网络流构造一个度数合法的方案,从而跑出对应的欧拉回路。

代码
#include <iostream>
#include <queue>
using namespace std;
const int inf = 2147483647;
int n, m;
struct Edge {
    int u, v, x, y;
} E[2005];
namespace Dinic {
    int S, T;
    int head[3005], nxt[200005], to[200005], res[200005], ecnt = 1;
    int cur[3005];
    void add(int u, int v, int ww) {
        to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, res[ecnt] = ww;
        to[++ecnt] = u, nxt[ecnt] = head[v], head[v] = ecnt, res[ecnt] = 0;
    }
    queue<int> q;
    int dep[3005];
    bool bfs() {
        for (int i = 1; i <= T; i++) dep[i] = -1;
        dep[S] = 1;
        q.push(S);
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            for (int i = head[x]; i; i = nxt[i]) {
                int v = to[i];
                if (dep[v] == -1 && res[i]) {
                    dep[v] = dep[x] + 1;
                    q.push(v);
                }
            }
        }
        return (dep[T] != -1);
    }
    int dinic(int x, int flow) {
        if (x == T) 
            return flow;
        int ret = 0;
        for (int i = cur[x]; i && flow; i = nxt[i]) {
            cur[x] = i;
            int v = to[i];
            if (dep[v] == dep[x] + 1 && res[i]) {
                int tmp = dinic(v, min(res[i], flow));
                res[i] -= tmp;
                res[i ^ 1] += tmp;
                flow -= tmp;
                ret += tmp;
            }
        }
        if (ret == 0) 
            dep[x] = -1;
        return ret;
    }
    int dinic() {
        int ret = 0;
        while (bfs()) {
            for (int i = 1; i <= T; i++) cur[i] = head[i];
            ret += dinic(S, inf);
        }
        return ret;
    }
    void ini() {
        ecnt = 1;
        for (int i = 1; i <= T; i++) head[i] = 0;
    }
}
int deg[1005];
bool chk(int k) {
    Dinic::ini();
    for (int i = 1; i <= m; i++) {
        Dinic::add(Dinic::S, i, 1);
        if (E[i].x <= k) 
            Dinic::add(i, E[i].v + m, 1);
        if (E[i].y <= k) 
            Dinic::add(i, E[i].u + m, 1);
    }
    for (int i = 1; i <= n; i++) Dinic::add(i + m, Dinic::T, deg[i] / 2);
    return Dinic::dinic() == m;
}
namespace Euler {
    int head[1005], nxt[2005], to[2005], id[2005], ecnt;
    void add(int u, int v, int x) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt, id[ecnt] = x; }
    bool ban[2005];
    int stk[2005], sz;
    void dfs(int x, int in) {
        for (int i = head[x]; i; i = nxt[i]) {
            head[x] = nxt[i];
            if (ban[i]) 
                continue;
            ban[i] = 1;
            int v = to[i];
            dfs(v, i);
        }
        in ? (stk[++sz] = in) : 0;
    }
    void work() {
        dfs(1, 0);
        for (int i = sz; i; i--) cout << id[stk[i]] << " ";
        cout << "\n";
    }
}
signed main() {
    cin >> n >> m;
    Dinic::S = n + m + 1;
    Dinic::T = n + m + 2;
    for (int i = 1; i <= m; i++) {
        cin >> E[i].u >> E[i].v >> E[i].x >> E[i].y;
        deg[E[i].u]++;
        deg[E[i].v]++;
    }
    int l = 1, r = 1000, mid, ans = -1;
    while (l <= r) {
        mid = (l + r) >> 1;
        if (chk(mid)) 
            ans = mid, r = mid - 1;
        else 
            l = mid + 1;
    }
    if (ans == -1) {
        cout << "NIE\n";
        return 0;
    }
    cout << ans << "\n";
    chk(ans);
    for (int i = 1; i <= m; i++) {
        for (int j = Dinic::head[i]; j; j = Dinic::nxt[j]) {
            if (Dinic::to[j] > n + m) 
                continue;
            if (Dinic::res[j] == 0) {
                if (Dinic::to[j] - m == E[i].v) 
                    Euler::add(E[i].u, E[i].v, i);
                else 
                    Euler::add(E[i].v, E[i].u, i);
            }
        }
    }
    Euler::work();
    return 0;
}

标签:cnt,return,int,1000005,ecnt,POI,include
From: https://www.cnblogs.com/forgotmyhandle/p/18146896

相关文章

  • POI2009SLO-Elephants
    #POI#Year2009#贪心#数学建图,对于每个环,有两种可行的方案,是这个环内部操作,需要的代价为\(mi\times(cnt-2)\),\(mi\)为这个环中的最小值,\(cnt\)为这个环的长度还可以用环外的一个点,需要的代价为\(mn\times(cnt+1)+mi\)直接贪心即可//Author:xiaruizeconsti......
  • POI2009LYZ-Ice Skates
    POI#Year2009#线段树#Hall定理考虑实际上是一个二分图匹配问题,那么这个二分图存在匹配当且仅对于\(L\)的任何子集右侧的度数和\(\geq\)左侧的然后线段树维护左侧的区间最大和//Author:xiaruizeconstintN=2e5+10;intn,m,k,d;structsegment_tree{#de......
  • POI2009GAS-Fire Extinguishers
    POI#Year2009#贪心贪心的把灭火器放到深度较小的点上,对于每个点,维护两个数组,记录距离当前点为\(x\)没有覆盖的点有\(a_x\)个,距离当前点\(y\)的灭火器有\(b_y\)个然后在每个点上,合并长度为\(len\)或者\(len-1\)的路径,因为这些路径不能延伸到父节点,所以要在这个点解决......
  • POI2010TEL-Teleportation
    分层图#贪心#POI#Year2010考虑将答案的图建成一个\(5\)层的图,其中\(1,2\)为第\(1,5\)层,第\(2,4\)层为已经与\(1,2\)相连的点考虑将剩下的点与第\(2,4\)层相连,贪心选尽可能大的//Author:xiaruizeconstintN=2e5+10;intn,m;vector<int>g[N];intcn......
  • POI2010MOT-Monotonicity2
    线段树#dp#线段树优化dp#POI#Year2010线段树维护\(dp\)转移即可//Author:xiaruizeconstintN=1e6+10;structsegment_tree{#definelsp<<1#definersp<<1|1 piimx[N<<2]; voidupdate(intp,intl,intr,intx,piiv) { if(l......
  • POI2010CHO-Hamsters
    POI#Year2010#kmp#字符串#dp#矩阵优化dp用\(kmp\)处理两个串拼在一起最小增加的代价,然后\(dp_{i,j}\)表示选择\(i\)个最后是\(j\)的最小长度转移枚举拼接的串这个明显可以矩阵优化//Author:xiaruizeconstintN=1e5+10;intn,m;strings[N];struc......
  • 解决.Net6 部署到ubuntu22.04中使用DotNetCore.NPOI 导出报 Could not open display (
    在Ubuntu22环境下,出现"Couldnotopendisplay(X-Serverrequired.CheckyourDISPLAYenvironmentvariable)"错误可能是由于缺少X服务器或未正确配置DISPLAY环境变量导致的。以下是你可以尝试的解决方法:检查DISPLAY环境变量:确保DISPLAY环境变量已正确设置。使......
  • P3523 [POI2011] DYN-Dynamite
    P3523[POI2011]DYN-Dynamite二分+树上贪心首先这题可以二分\(K\),转化为判定性问题:是否存在\(m\)个点使得所有关键节点的\(dis\leK\)。那么意思就是,每个点可以控制\(K\)距离以内的关键点。那么我们可以从叶子节点向上贪心,实在覆盖不到了再选点。那么我们要判断该点是......
  • dbt-checkpoint 确保dbt 项目质量的pre-commit hooks 工具
    dbt-checkpoint实际上属于pre-commithooksplugin实现了不少hooks可以用来提升dbt项目的模型质量内部处理上实际是对于dbt的元数据进行解析,当然dbt-checkpoint也提供了不少其他扩展目前包含的hooks只大概说明下,详细的后边介绍下,目前涉及了,model,source,script,macro,modifier......
  • dbt-checkpoint 源码结构简单说明
    前边说过dbt-checkpoint是基于dbt的元数据解析,然后集合规则进行check,属于一个pre-commit插件,以下简单说明下内部实现配置核心是.pre-commit-hooks.yaml文件,一个标准的pre-commit定义内容核心是id,name,entry,language,entry实际上就是一个pythonentry_points的console_......