首页 > 其他分享 >USACO22DEC P【杂题】

USACO22DEC P【杂题】

时间:2023-01-05 20:44:14浏览次数:48  
标签:int tt leq USACO22DEC 条边 mathcal 杂题 复杂度

A. [USACO22DEC] Breakdown P

给定一个\(n\) 个点 \(n^2\) 条边的有向完全图,边有边权 \(w_{i,j}\)。\(n^2\) 次操作,每次操作删去一条边,每次操作后询问从 \(1\) 到 \(n\) 可以重复经过点边的恰好 \(k\) 条边的最短路长度。

\(n \leq 300\),\(k \leq 8\),\(w_i \leq 10^8\)。


显然考虑倒过来加边。

注意到单次询问可以 \(\mathcal{O}(n)\) 解决,于是可以考虑 meet-in-middle,需要维护 \(f_{i,u},g_{i,u}(1 \leq i \leq 4)\) 分别表示从 \(1\) 到 \(u\) 和从 \(u\) 到 \(n\) 经过 \(i\) 条边的最短路,这样就能拼起来了。

接下来的问题就变成了如何维护 \(f,g\)。\(4\) 条边还是有点多,我们考虑再做一次折半,维护 \(h_{i,u,v}(1 \leq i \leq 2)\) 表示从 \(u\) 到 \(v\) 经过 \(i\) 条边的最短路,这两者都容易在 \(\mathcal{O}(n)\) 的时间复杂度内更新。

以 \(f\) 为例:当 \(x \neq 1\) 时,\(f\) 的路径一定会被分成长度不超过 \(2\) 的两段,可以直接利用 \(f\) 和 \(h\) 拼出来。只有当 \(x=1\) 时且 \(i = 4\) 时,我们需要确定长度为 \(3\) 的路径的形态,枚举中间点即可。虽然这部分时间复杂度为 \(\mathcal{O}(n^2)\),但 \(x=1\) 的边只有 \(\mathcal{O}(n)\) 条,因此总时间复杂度为 \(\mathcal{O}(n^3)\),可以通过本题。

code
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> pi;
const int N = 3e2 + 5, inf = 1e9;
int n, k, f[5][N], g[5][N], h[3][N][N], e[N][N];
pi q[N * N];
int ans[N * N];
void chk(int &x, int y) {
    if (x > y) x = y;
}
int main() {
    ios :: sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> k;
    int l = k / 2, r = k - l;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            cin >> e[i][j];
    for (int i = 1, x, y; i <= n * n; i++) {
        cin >> x >> y;
        q[i] = {x, y};
    }
    for (int j = 1; j <= 4; j++) 
        for (int i = 1; i <= n; i++)
            f[j][i] = g[j][i] = inf;
    for (int k = 1; k <= 2; k++)    
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                h[k][i][j] = inf;
    for (int t = n * n, x, y, z; t >= 1; t--) {
        tie(x, y) = q[t], z = e[x][y];
        chk(h[1][x][y], z);
        for (int i = 1; i <= n; i++) {
            chk(h[2][x][i], z + h[1][y][i]), chk(h[2][i][y], h[1][i][x] + z);
        }   
        for (int j = 1; j <= 2; j++) 
            for (int i = 1; i <= n; i++)
                chk(f[j][i], h[j][1][i]), chk(g[j][i], h[j][i][n]);
        if (x == 1) {
            for (int i = 1; i <= n; i++)
                chk(f[3][i], z + h[2][y][i]);
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    chk(f[4][j], z + h[2][y][i] + h[1][i][j]);
        }
        if (y == n) {
            for (int i = 1; i <= n; i++)
                chk(g[3][i], h[2][i][x] + z);
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    chk(g[4][j], h[1][j][i] + h[2][i][x] + z);
        }
        for (int i = 1; i <= n; i++) {
            chk(f[3][i], f[1][x] + z + h[1][y][i]);
            chk(g[3][i], h[1][i][x] + z + g[1][y]);
            chk(f[4][i], f[1][x] + z + h[2][y][i]);
            chk(f[4][i], f[2][x] + z + h[1][y][i]);
            chk(g[4][i], h[2][i][x] + z + g[1][y]);
            chk(g[4][i], h[1][i][x] + z + g[2][y]);
        }
        chk(f[3][y], f[2][x] + z);
        chk(g[3][x], z + g[2][y]);
        chk(f[4][y], f[3][x] + z);
        chk(g[4][x], z + g[3][y]);
        int ret = inf;
        for (int i = 1; i <= n; i++)
            ret = min(ret, f[l][i] + g[r][i]);
        ans[t] = ret;
    }
    for (int i = 2; i <= n * n; i++) {
        if (ans[i] == inf) {
            cout << -1 << "\n";
        } else {
            cout << ans[i] << "\n";
        }
    }
    if (n == 1) {
        cout << 0 << "\n";
    } else {
        cout << -1 << "\n";
    }
    return 0;   
}

B. [USACO22DEC] Making Friends P

给定一个 \(n\) 个点 \(m\) 条边的无向图,按照从 \(1\) 到 \(n\) 的顺序依次删点,删掉 \(i\) 时把所有与 \(i\) 相邻的点两两之间连边(若已经有边则不再连)。求最终会新连多少条边。

\(n,m \leq 2 \times 10^5\)。


我们给所有边定一个方向:对于边 \((u,v)(u < v)\),规定其方向为 \(u \to v\)。

考虑换一种方式统计答案:只在删除 \(i\) 时统计 \(i\) 的出边数量,其正确性显然。

接下来考虑如何维护操作过程。我们对于每个点 \(i\) 维护其出边集合 \(e_i\),由于我们只需要在删除 \(i\) 时统计 \(i\) 的出边数量,因此我们可以将一些边 “延迟加入”,以达到减少复杂度的目的。具体来说是这样的:删除点 \(i\) 时,我们取出 \(e_i\) 中的最小元素 \(v\),然后将 \(e_i\) 内的其他元素并入 \(e_v\) 即可。

该做法的正确性不难验证。使用 set 启发式合并容易维护上述过程,时间复杂度 \(\mathcal{O}(n \log^2 n)\)。

code
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long LL;
int n, m;
set <int> e[N];
int main() {
    ios :: sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n >> m;
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        if (x > y) swap(x, y);
        e[x].insert(y);
    }
    LL ans = 0;
    for (int i = 1; i <= n; i++) {
        if (e[i].size() == 0)
            continue;
        ans += e[i].size();
        int u = *e[i].begin(), v = i; e[v].erase(e[v].begin());
        if (e[u].size() < e[v].size()) {
            swap(e[u], e[v]);
        }
        for (auto x : e[v]) {
            e[u].insert(x);
        }
    }
    ans -= m;
    cout << ans << "\n";
    return 0;   
}

C. [USACO22DEC] Palindromes P

给定一个 \(01\) 串 \(S\),定义一个 \(01\) 串的权值为通过交换相邻元素到达回文串需要的最小操作次数,如果无法到达则定义为 \(-1\)。求 \(S\) 的所有子串的权值之和。

\(n \leq 7500\)。


先考虑如果给定一个 \(01\) 串,如何求其权值。

不妨只考虑 \(\tt 1\) 的对称。显然,我们不会交换两个 \(\tt 1\) 的位置,因此所有 \(\tt 1\) 的相对位置不会发生改变。另一个显然的事实是无解当且仅当 \(\tt 1\) 有奇数个,且总长度为偶数。

考虑找到每一对对应的 \(\tt 1\)(如果存在中间的 \(\tt 1\) 则单独考虑),对于第 \(i\) 对 \(\tt 1\),设左边的 \(\tt 1\) 是从左数第 \(l_i\) 个,右边的是从右数第 \(r_i\) 个,使它们对应所需要的操作次数为 \(|l_i-r_i|\)。因此答案即为所有 \(|l_i - r_i|\) 之和。

直接计算是 \(\mathcal{O}(n^3)\) 的,考虑优化。由于我们要求的东西是一个对称的形式,考虑枚举中点(可能是一个 \(\tt 1\) 或在两个 \(\tt 1\) 之间),然后往两边拓展,途中计算每一对 \(l_i,r_i\) 的贡献。

对于一对 \(\tt 1\),设其从左至右的编号为 \(l_i,r_i\),那么对于区间 \([l,r](l < l_i,r > r_i)\),它的贡献为 \(|(l_i - l + 1) + (r - r_i + 1)| = |l_i + r_i - (l+r)|\)。注意到其中的变量只有 \(l+r\),我们不妨设 \(f_i(s) = |l_i + r_i - s|\)。则对于区间 \([l,r]\),其答案为 \(\sum \limits_{i=1}^k f_i(l+r)\),其中 \(k\) 为这个区间内的 \(\tt 1\) 的对数。我们将其记为 \(F(l+r)\)。

我们可以将 \(k\) 相同的区间一起考虑,即把 \(l,r\) 分别固定在区间 \((l_{i+1},l_i],[r_i,r_{i+1})\) 内。接下来我们需要解决的问题是 \(l,r\) 移动时 \(F(l+r)\) 的变化。

具体来说,以 \(F(s) \to F(s+1)\) 为例,当 \(s \gets s+1\) 时,对于所有 \(l_i+r_i \geq s+1\) 的 \(f_i(s)\) 要 \(-1\),其余则要 \(+1\)。不难发现 \(F(l+r)\) 可以按照 \(l_i + r_i\) 分成若干段,每一段内都是一个一次函数,因此我们只需要维护差分 \(d(s) = F(s+1) - F(s)\) 即可。易知 \(d(s+1) - d(s) = 2c_{s+1}\),其中 \(c_s\) 表示 \(l_i + r_i = s\) 的对数。

虽然看起来共有 \(\mathcal{O}(n)\) 个中心,每个中心最高会有 \(\mathcal{O}(n^2)\) 复杂度的计算,但因为我们只是枚举了所有的子串,所以复杂度是 \(\mathcal{O}(n^2)\) 的,可以通过本题。

code
#include <bits/stdc++.h>
using namespace std;
const int N = 8e3 + 5;
typedef long long LL;
int n, m, p[N], c[N << 1];
string s;
int main() {
    ios :: sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> s;
    n = s.length();
    for (int i = 1; i <= n; i++) {
        if (s[i - 1] == 'G') p[++m] = i;
    }
    p[0] = 0, p[m + 1] = n + 1;
    LL ans = 0;
    for (int L = 1; L <= m; L++) {
        for (int R = L; R <= L + 1 && R <= m; R++) {
            int d = 0, f = 0;
            memset(c, 0, sizeof(c));
            for (int i = 0; L - i >= 1 && R + i <= m; i++) {
                int l1 = p[L - i], l2 = p[L - i - 1];
                int r1 = p[R + i], r2 = p[R + i + 1];
                if (l1 < r1) {
                    c[l1 + r1] += 1, d += 1;
                }
                for (int l = l1; l > l2; l--) {
                    for (int r = r1; r < r2; r++) {
                        if (L == R) {
                            if ((r - l + 1) % 2 == 1) {
                                ans += (f + abs((l + r) / 2 - p[L]));
                            } else {
                                ans -= 1;
                            }
                        } else {
                            ans += f;
                        }
                        f += d, d += 2 * c[l + r + 1];
                    }
                    for (int r = r2; r >= r1; r--) {
                        d -= 2 * c[l + r];
                        f -= d;
                    }
                }
                for (int r = r1; r < r2; r++) {
                    f += d;
                    d += 2 * c[l2 + r + 1];
                }
            }
        }
    }
    cout << ans << "\n";
    return 0;   
}

标签:int,tt,leq,USACO22DEC,条边,mathcal,杂题,复杂度
From: https://www.cnblogs.com/came11ia/p/17027673.html

相关文章

  • [USACO22DEC] Cow College B 题解
    洛谷P8897AcWing4821题目描述有\(n\)头奶牛,每头奶牛愿意交的最大学费位\(c_i\),问如何设置学费,可以使赚到的钱最多。\(1\len\le10^5,1\lec_i\le10^6\)做法分析......
  • USACO22DEC青铜组题解
    T1:CowCollege总学费\(=\)设置的单人学费\(\times\)接受的奶牛数一旦固定单人学费,就能确定接受的奶牛数单人学费可以是哪些值?\(\{c_1,c_2,\cdots,c_n\}\)其中......
  • 「杂题乱写」AtCoderDP26 题
    「杂题乱写」AtCoderDP26题\(\text{AtCoderDP26}\)题题单。前言最近听说\(\text{AtCoder}\)上有个\(\text{DP26}\)题挺好的,于是向@\(\text{SoyTony}\)要了题单......
  • 组合杂题选讲 #7
    题目描述题意:每次抽卡时会从\(n\)种卡里随机获得一种,那么期望上抽到全部\(n\)种卡需要抽多少次?提示:随机试验中某个变量的数学期望(简称“期望”)是指该变量所有可能的......
  • 组合杂题选讲 #6
    题目描述题意:请证明,平面上不存在\(4\)个点,使得任意两个点之间的距离均为奇整数。提示:这里所说的距离是指欧几里得距离,即点\((x_1,y_1)\)和\((x2,y_2)\)之间的距离......
  • 组合杂题选讲 #5
    题目描述题意:平面上有红色点和蓝色点各\(n\)个,且这\(2n\)个点没有三个点共线。我们称一种红蓝点之间的配对方案合法,是指在每对点之间用线段连接后,得到的\(n\)条线段......
  • 组合杂题选讲 #4
    问题描述题意:已知有一个\(n\)点的无向图\(G\)不包含三元环,求\(G\)边数的最大值。提示:设\(G=(V,E)\)是一个无向图。我们称\(G\)包含三元环是指存在三个点\(a,b......
  • 数据结构杂题选做
    好像数据结构也没什么专项,那就全塞一起吧(大雾好像wind_whisper神仙今天留的题也没什么专项。P1972[SDOI2009]HH的项链居然没做过的“经典题”++。怎么到处都是经典题......
  • DP 杂题选做
    概率期望DP学习笔记树形DP学习笔记其余就不具体分类了。P1220关路灯题解说这是区间DP经典题,但我以前居然没听说过,这下尴尬了。设\(f_{i,j,0/1}\)表示关掉区......
  • 组合杂题选讲 #3
    问题描述题意:现在有\(n\)个人要成立若干个社团(一个人可以属于多个社团)满足每个社团的人数均为奇数;任意两个不同的社团所共有的成员数量为偶数。求证:所能成立的社团......