codeforce刷题(六)

G. Maximize the Remaining String

由小写字母组成的字符串\(s\),每次从中选取重复的字母进行删除,直到该字符串中的每个字母都只出现一次。问:最终形成的字典序最大的字符串,比如\(ababab\),答案为\(ba\)

\(1 \leq len(s) \leq 200000\)

题解

\(s=a_1a_2a_3\cdots a_n\),最后的字符串为\(s'\),它的首字符在\(s\)中对应位置为\(p\),显然\(p\)之前的字符都被删除了,并且被删除的字符在\(p\)位置之后都出现过,根据这个性质,二分枚举\(p\)。假设子串\(a_1a_2\cdots a_p\)中字典序最大的字符为\(a_i\):

  • \(a_i > a_p\),则字符\(a_i\)作为\(s'\)的首字符,更新\(s=a_{i + 1}a_{i+2}a_{i+3}\cdots a_n\) - \(\{a_i\}\),(减号表示删除前一个字符串中所有的后一个字符)
  • \(a_i == a_p\),则字符\(a_i\)作为\(s'\)的首字符,更新\(s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n\) - \(\{a_p\}\)
  • \(a_i < a_p\),则字符\(a_p\)作为\(s'\)的首字符,更新\(s=a_{p + 1}a_{p+2}a_{p+3}\cdots a_n\) - \(\{a_p\}\)

\(a_i > a_p\)时,如果有多个\(a_i\),显然选择第一次出现的\(a_i\)更优

通过不断的枚举\(p\)确定\(s'\)中的每个字符,且枚举次数不大于26次。

复杂度\(O(26*n*log(n))\)

E. K-periodic Garland

由0,1组成的字符串\(s\),如果\(s\)中任意相邻字符'1'之间的距离为\(K\),则该字符串是良好的。将 '0' \(\rightarrow\) '1' (或者 '1' \(\rightarrow\) '0') 称为一次操作,问最少几次操作后,能将\(s\)变成良好的?

注意:'00'、'0100' 对任意的\(K\)都是良好的,\(1 \le K \le len(s) \le 1e6\)

题解

记最终良好的字符串为\(str\),考虑\(s\)中从左至右第一个 '1' 出现的位置,记为\(pos\),显然,\(str\)中从左至右第一个 '1' 出现的位置不会小于\(pos\),那么可以求得以\(pos\)作为\(str\)中从左至右第一个 '1' 出现的位置且满足良好的最少操作次数。如果不是\(pos\),那么会不会是\(s\)中第一个 '1' 和 第二个 '1' 之间的某个位置\(P\)呢?假设\(str\)满足上述条件,以\(P\)位置作为\(str\)中第一个 '1' 出现的位置,这样做的话需要 \(t\) 次操作,而令\(s[pos] \rightarrow 0\),既让\(str\)中第二个 '1' 作为第一个 '1',所需要的操作次数至多为 \(t - 1\),故这样的\(P\)显然不应该考虑。综上所述,我们可以枚举\(str\)的第一个'1' 出现的位置,复杂度\(O(\frac{n^2}{K})\)。对数论稍微敏感一点儿就会发现对\(K\)的加法将这些位置划分为了\(K\)类,什么意思呢?假设\(K = 3\)

\(i\)
0 3 6 9 12 15
1 4 7 10 13 16
2 5 8 11 14 17
3 6 9 12 15 18
4 7 10 13 16 19

如果计算出了\(pos = 0\)的答案,相当于也就计算出了\(pos = 3,6,9,12\)的答案。所以只需要计算\(K\)次,每次的复杂度为\(O(\frac{n}{K})\),故总的复杂度为\(O(n)\)

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,no-stack-protector,fast-math")
// cnt[j] := 以 j 位置作为 str 中第一个 ‘1’ 的位置且使得 s 变为 str 所需要的最少次数
#include <bits/stdc++.h>
#define IO ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0)
using namespace std;

const int maxn = 1e6 + 5;

int t, n, k;

int main()
{   
    cin >> t;
    while (t--)
    {   
        cin >> n >> k;
        string s;
        cin >> s;

        vector<int> use(2 * n + 5, 0);
        vector<int> cnt(2 * n + 5, 0);

        int pos = -1;
        for (int i = n - 1; ~i; i--) if (s[i] == '1') {
            pos = i;
            break;
        }

        if (pos == -1) {
            cout << 0 << endl;
            continue;
        }

        for (int i = 0; i < pos; i++) cnt[i] = -1;

        use[n - 1] = (s[n - 1] == '1');
        for (int i = n - 2; ~i; i--) {
            if (s[i] == '1') use[i] = 1;
            use[i] += use[i + 1];
        }

        int ans = n << 1 + 1;
        for (int i = n - 1; ~i; i--) if (s[i] == '1') {

            int tp = 0;

            for (int j = i + k; ; j += k) {

                tp += (use[j - k + 1] - use[j] + 1);

                if (cnt[j] != -1) {
                    tp = tp - 1 + cnt[j];
                    if (use[i + 1]) tp = min(tp, use[i + 1]);     // 直接抹掉 i 位置之后的所有的 ‘1’,可能较优。例如 K = 1,s = 10001
                    break;
                }
                if (j >= n) break;
            }
            cnt[i] = tp;
            ans = min(ans, tp + use[0] - use[i]);
        }
        cout << ans << endl;
        /* code */
    }
    
    return 0;
}

wonderful, BFS

给一个01矩阵\(A_{m*n}\),有\(q\)次询问:\(x,y\),问离\((x,y)\)最近的 1 的曼哈顿距离。

\(1 \leq n,m \leq 1000\)\(1 \leq q \leq 1e5\)

题解

加一个超级源点\(s\),与所有的 1 连接起来,然后以\(s\) 作为起点,BFS遍历图的同时更新距离即可。复杂度\(O(nm)\)【单纯的记录一哈,orz

queue < pair<int, int> > qe;

void BFS() {
    memset(d, -1, sizeof(d));
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (mp[i][j]) {
        d[i][j] = 0;
        qe.push(make_pair(i, j));
    }
    while(!qe.empty()) {
        pair<int, int> p = qe.front();
        qe.pop();
        for (int i = 0; i < 4; i++) {
            int u = p.first + dx[i], v = p.second + dy[i];
            if (u < 0 || v < 0 || u >= n || v >= m || d[u][v] != -1) continue;
            d[u][v] = d[p.first][p.second] + 1;
            qe.push(make_pair(u, v));
        }
    }
}

1349C - Orac and Game of Life【好题】

给一个长度为\(n\)的序列,每次选择一个区间\([l,r]\)并将这个区间包含的元素都替换为该区间的中位数(按照大小关系排在中间的数)。问最终是否能将整个序列中的元素都替换为\(k\)

\(1 \leq n \leq 1e5\)\(1 \leq a_i, k \leq 1e9\)

题解

  • 原始序列不包含\(k\),显然无解

  • 如果原始序列中存在一个区间,其中位数为\(k\),则最终肯定能够将整个序列替换为\(k\)

  • 如果原始序列不存在上述区间呢?构造

在原序列中直接找一个中位数为\(k\)的区间,想了很久都没想到一个好的办法。其实,着眼于细微之处,如果序列中存在相邻的两个数为\(k,a_i\)或者\(a_i,k\),有\(a_i \geq k\),那么显然这是一个中位数为\(k\)的区间。如果不存在这样的相邻两个数,既对任意的\(k\),其左右相邻的两个数都小于\(k\)\(a_i,k,a_j\),都有\(a_i < k\)\(a_j < k\)),那么只需要将其中的任意一个(\(a_i\)或者\(a_j\))替换为大于或者等于\(k\)的数就行,等价于找一个中位数大于或者等于\(k\)的区间。怎么找呢?【orz】其实只需要找到一个区间\((a_e,a_{e+1},a_{e+2})\),其中至少存在两个大于\(k\)的数就行。为什么只需要找长度为3区间呢?假设存在一个区间\([l,r]\)其中位数为\(p\)\(p\geq k\),说明这个区间大于或者等于\(p\)的元素至少有\(\frac{r-l+1}{2} + 1\)个,既大于或者等于\(k\)的数至少超过一半,显然,至少存在一个长度为3的区间其中位数必定大于或者等于\(k\)。证明的话,将区间划分成一个一个长度为3且不相交的区间,然后对最后剩下的一个区间分类讨论即可。如果找不到上述的长度为3且满足条件的区间,显然不可能将\(a_i\)或者\(a_j\)替换为大于或者等于\(k\)的数。

1349A - Orac and LCM

给一个长度为\(n\)的序列,求\(gcd(\{lcm(a_i,a_j)|1 \leq i,j \leq n,i \neq j\})\)

\(1 \leq n,a_i \leq 2e5\)

题解

想到了枚举最终答案的约数,但等于圈圈。记最终答案为\(ans\)\(p\)表示质数,注意:如果\(p^k | ans\),那么至少存在\(n-1\)\(a_i\),有\(p^k|a_i\)

反证法很好证明。知道这个特点之后怎么做呢?直接的思路依然是枚举,然而!求\(n-1\)\(a_i\)\(gcd\)不就找到了\(p^k\)吗,妙得一匹。记\(gcd(a_i) = gcd(\{a\} - \{a_i\})\),那么\(ans = lcm(gcd(a_1), gcd(a_2), gcd(a_3),\cdots,gcd(a_n))\).

1346F - Dune II: Battle For Arrakis

给一个棋盘\(A_{n*m}\),每个格子上有\(a_{ij}\)枚棋子,记\(cnt\)为 将所有的棋子移到同一个格子上所需要的最少次数,一次只能将一枚棋子移动一步(上下左右)。现给\(k\)次修改:将\(a_{ij} \rightarrow b_{ij}\),问每次修改后需要的最少次数,既\(cnt\)

\(1 \leq n,m,k \leq 1000\)

题解

我的想法:研究修改后的最佳位置与先前最佳位置的关系,然并卵

记最佳位置为\((x,y)\),有

\[\begin{align} cnt =& \sum_{i=1}^{n}\sum_{j=1}^m(|x-i|+|y-j|)*a_{ij} \\ =& (\sum_{i=1}^n\sum_{j=1}^m|x-i|*a_{ij}) + (\sum_{i=1}^n\sum_{j=1}^m|y-j|*a_{ij}) \\ =& (\sum_{i=1}^n(|x-i|*\sum_{j=1}^ma_{ij})) + (\sum_{j=1}^m(|y-j|*\sum_{i=1}^na_{ij})) \\ \end{align} \]

\(A_i = \sum_{j=1}^ma_{ij}\), \(B_j = \sum_{i=1}^na_{ij}\),有

\[cnt = \sum_{i=1}^n|x-i|*A_i + \sum_{j=1}^m|y-j|*B_j \]

这是一个最优化问题,最小化\(cnt\),也就是分别最小化\(\sum_{i=1}^n|x-i|*A_i\)\(\sum_{j=1}^m|y-j|*B_j\)

重点来了,最小化\(\sum_{i=1}^n|x-i|\)比较好做,在最中间任取一个数就行。那多了一部分\(A_i\)该怎么做喃?在脑海中抽象一个模型,\(x\)轴上有一些球落在\(x_i\)处,记\(x_i\)处的球的个数为\(A_i\),然后有一个挡板\(X\)也在\(x\)轴上,问\(X\)的最佳位置,使得\(\sum_{i=1}^n|X-x_i|*A_i\)最小。本质上与最小化\(\sum_{i=1}^n|x-i|\)是同一个问题,所以\(X\)的位置依然是在最中间。同样的道理可以求得\(y\)的位置,知道\((x,y)\)后就可以以\(O(1000)\)的复杂度求得\(cnt\),最终复杂度为\(O(max(n,m)*k)\)

Palindrome(好题)

给一个字符串\(s\),问它有多少个子串是好子串?一个子串被称为好子串\(S\)当且仅当它的长度为\(3n-2\)并且\(S[i] = S[2n-i] = S[2n + i - 2]\)\((1 \leq i \leq n)\).

\(1 \leq len(s) \leq 500000\)

题解

分析\(S[i] = S[2n-i] = S[2n + i - 2]\),可以发现对于一个长度为\(3n-2\)的好子串\(S\),有:

  • \(S_{123...(2n-1)}\)是一个回文串并且中心为\(n\),半径为\(n-1\)
  • \(S_{n(n+1)(n+2)...(3n-2)}\)是一个回文串并且中心为\(2n-1\),半径为\(n-1\)

枚举前一个回文串的中心和半径,那么后一个回文串的中心和半径也就确定了。先用马拉车算法预处理出每个位置的回文半径\(len[pos]\)。考虑已经枚举到\(s\)的第\(i\)位置,该位置的回文半径为\(len[i]\),如果\(len[i+len[i]] \geq len[i]\),那么一个好子串就出现了,所以需要考虑以\(i\)位置为中心的所有回文子串,检查是否有合法的后一个回文串,既\(len[i + len[i] - t] \geq len[i] - t\)\((0 \leq t \leq len[i] - 1)\).

显然,不能直接暴力的检查 以\(i\)位置为中心的所有回文子串是否有合法的后一个回文串。观察上面这个不等式,移项:

\[len[i + len[i] - t] + t \geq len[i] ~~~~~~~~~~~~~~~(0 \leq t \leq len[i] - 1) \]

重点来了,令\(p[i] = n - i + 1\)\((1\leq i \leq n)\),那么合法的后一个回文串的个数就等于在\(\sum_{j=i+1}^{i+len[i]}((len[j] + p[j]-p[i+len[i]])\geq len[i])\),移项:

\[\sum_{j = i + 1}^{i+len[i]}(len[j] + p[j] \geq p[i + len[i]] + len[i]) \]

求该和式等价于求一个区间\([l,r]\)内,大于或者等于\(K\)的数有多少个?方法比较多,主席树,线段树,离线树状数组。

/* 树状数组 */
int n, m;
int len[maxn << 1], b[maxn];

char s[maxn << 1], s1[maxn];

pair<int, int> a[maxn];

void change() {
    for (int i = 1, t = 0; i <= n; i++) {
        s[++t] = '#';
        s[++t] = s1[i];
    }
    s[0] = '$', s[2 * n + 1] = '#', s[2 * n + 2] = '@', s[2 * n + 3] = '\0';

    n = 2 * n + 3;
}

void ma() {
    int id = 0;
    for (int i = 1; i <= n; i++) {
        int t = i > len[id] + id ? t = 0 : min(len[2 * id - i], id + len[id] - i);
        while(s[i + t] == s[i - t]) t++;
        len[i] = t - 1;
        if (id + len[id] <= i + len[i]) {
            id = i;
        }
    }
    id = 0;
    for (int i = 1; i <= n; i++) if (s[i] != '#') {
        len[++id] = len[i] / 2;
    }
}

struct query {
    int l, r, x;
    bool operator < (const query& i) const {
        if (x == i.x) return l < i.l;
        return x < i.x;
    }
} q[maxn];

int lowbit(int x) {
    return x & (-x);
}

void add(int i, int x) {
    while(i <= m) {
        b[i] += x;
        i += lowbit(i);
    }
}

int sum(int i) {
    int res = 0;
    while(i > 0) {
        res += b[i];
        i -= lowbit(i);
    }
    return res;
}

void solve() {
    int t = 0;
    for (int i = 1; i <= m; i++) if (len[i]) {
        q[t].l = i + 1;
        q[t].r = i + len[i];
        q[t++].x = len[i] + m - i - len[i] + 1;
    }

    for (int i = 1; i <= m; i++) {
        a[i].first = len[i] + m - i + 1;
        a[i].second = i;
    }

    sort(a + 1,  a + m + 1);
    sort(q, q + t);

    int j = 1;
    long long res = 0;
    for (int i = 0; i < t; i++) {
        while(a[j].first < q[i].x && j <= m) {
            add(a[j].second, 1);
            j++;
        }
        res += (q[i].r - q[i].l + 1) - (sum(q[i].r) - sum(q[i].l - 1));
    }

    printf("%lld\n", res);
}

int main()
{
    int cas;
    cin >> cas;
    while (cas--)
    {
        /* code */
        scanf("%s", s1 + 1);
    
        memset(len, 0, sizeof(len));
        memset(b, 0, sizeof(b));

        m = n = strlen(s1 + 1);
        change();
        ma();
    
        solve();

    }
    return 0;
}

1346E - Magic Tricks

\(n\)个位置,每个位置上都有一个球。初始时,第\(k\)个位置上是一个特殊的球。现给出\(m\)次交换,记\(t_i\)表示\(m\)次交换后,特殊的球被交换到了\(i\)位置的最少 \(fake ~ swap\) 次数。一次交换被称为\(fake ~ swap\) 当且仅当这次交换什么都没做,既不发生交换。求出所有\(t_i\)\((1 \leq i \leq n)\)

\(1 \leq n,m \leq 2e5\)

题解

时间是线性的,令\(dp[a][b]:=\) \(b\)次交换后,特殊的球被交换到\(a\)位置的最少\(fake ~ swap\)次数,有:

\[假设第i次交换为:(u,v)\\ dp[u][i] = min(dp[u][i - 1] + 1, dp[v][i - 1]) \\ dp[v][i] = min(dp[v][i - 1] + 1, dp[u][i - 1]) \]

1345D - Monopole Magnets

给一个\(n*m\)的棋盘,每个格子都有一种颜色,黑色或者白色,然后将一些\(N\)\(S\)放入棋盘的格子中,一个格子可以放入多个,且\(N\)\(S\)可以放在同一个格子里面。求最少需要放入\(N\)的个数(\(S\)的个数不限)使得满足如下三个条件:

  • 棋盘的每行每列至少有一个\(S\)
  • 对于黑色位置\((i,j)\),经过有限次操作后,至少存在一个\(N\)能到达这些位置。
  • 对于白色位置\((i,j)\),无论经过多少次的操作,都没有\(N\)能到达这些位置。

一次操作:选取任意两个在同行或者同列,但不在同一个格子里面的\(N\)\(S\),保持\(S\)的位置不变,\(N\)\(S\)靠近一步。

\(1 \leq n,m \leq 1000\)

题解

先判断不存在解的情况。如果\((i,j)\)位置是白色,且这个位置放\(S\),那么第\(i\)行和第\(j\)列不能存在黑色位置,因为既然能达到黑色位置,则经过有限次操作后,必定能到达\((i,j)\),而\((i,j)\)是不可达的。如果\((i,j)\)位置是黑色,且这个位置放\(S\),那么第\(i\)行和第\(j\)列只能存在一段连续的黑色位置,因为若存在两端或者更多段黑色位置,则段与段之间白色的部分必定可达。检查完所有的位置后,如果某行或者某列所有的位置都不能放\(S\),则解不存在。

经过上面的检查后,棋盘中黑色的位置组成了一个个连通块,在每个连通块的边界都放上\(S\),显然,一个连通块只需要在块内任意位置处放入一个\(N\)就行了。故最少的\(N\)的个数就是连通块的个数。

1343D - Constant Palindrome Sum

给一个长度为\(n\)的序列\(A\),问最少需要几次操作使得\(\forall i \in [1;\frac{n}{2}]\),有\(a_i + a_{n-i+1} = X\)

一次操作:将任意一个数置为区间\([1;k]\)内的数。

\(1 \leq a_i \leq k \leq 2e5\)\(1 \leq n \leq 2e5\),且\(n\)为偶数。

题解

\(x = min(a_i, a_{n - i + 1})\)\(y = max(a_i, a_{n - i + 1})\)。如果\(X\)落在区间:

  • \([2, x]\),第\(i\)对的贡献为2
  • \([x + 1, x + y - 1]\),第\(i\)对的贡献为1
  • \([x+y, x+y]\),第\(i\)对的贡献为0
  • \([x + y + 1, k + y]\),第\(i\)对的贡献为1
  • \([k + y + 1, 2 * k]\),第\(i\)对的贡献为2

显然,我们可以在每一对 \(i\) 所对应的这些区间上预先加上贡献,最后求区间\([2; 2*k]\)上的最小值就是最少操作次数。区间操作,单点查询,可以用树状数组。但还有比较简单的方法,利用差分:在区间\([l,r]\)上加1,则\(b[l] += 1\)\(b[r + 1] -= 1\),处理完所有的区间后,扫描一遍求前缀和,既\(b[i] += b[i - 1]\),最后\(answer = min\{b_i\}\)

1342C - Yet Another Counting Problem

给两个正整数\(a,b\),问在区间\([L,R]\)内有多少个数满足:\((x \% a) \% b ~~!= ~~(x\%b)\%a\)

\(1 \leq a, b, L, R \leq 1e18\)

题解

不妨假设\(a \leq b\),如果\((x \% a) \% b ~~= ~~(x\%b)\%a\),有:\(x \% a ~~= ~~(x\%b)\%a\),并且\(a | (x - x \% b)\)。假设\(x = p * b + r\),则有\(a |(p*b)\),既\(p\)\(\frac{a}{gcd(a,b)}\)的倍数,既\(p\)的取值为:\(\{0, \frac{a}{gcd(a,b)}, \frac{2a}{gcd(a,b)}, \frac{3a}{gcd(a,b)},... \}\),而\(r\)的取值为:\(\{0, 1, 2, 3, ...,b-1\}\)。所以区间\([0, R]\)内满足相等条件的\(x\)的个数为:\(\frac{(R+1)*gcd(a,b)}{ab} * b + min(b, (R+1)\%(\frac{ab}{gcd(a,b)}))\)

1342D - Multiple Testcases

给一个长度为\(n\)的序列\(A\)\(1 \leq a_i \leq k\),和长度为\(k\)的序列\(C\)\(c_1 \geq c_2 \geq c_3 \geq ... \geq c_k\),问\(A\)最少能被分成几组,使得每组都满足如下条件:

  • 记每组中大于或者等于\(i\) 的个数为\(g_i\),有:\(g_i \leq c_i\)

\(1 \leq n,k \le 2e5\)

题解

记最少组数为\(ans\),有:\(ans = max\lceil \frac{g_i}{c_i} \rceil\)。怎么构造呢,令\(a_i \in ans[i \% ans]\)\(i \in {0,1,2,...,n-1}\)。还是比较巧妙得!

1341D - Nastya and Scoreboard

\(n\)个数字显示器,问再打开\(k\)个显示器中导管后,这\(n\)个数字显示器所组成的最大整数?

\(1 \leq n, k \leq 2000\)

题解

\(dp[i][j]:=\) \(i\) 个数字显示器再打开\(j\)个导管时,第\(i\)个数字显示器显示的最大数字。为了满足最大的整数,第一个显示器显示的数字应该尽可能的大,实际上就是\(dp[0][k]\)。从后向前递归,**记录所有合法的状态\(dp[i][j]\) **,再从前向后回溯就能找到所有合法的显示器的数字了。

string s[10] = {"1110111", "0010010", "1011101", "1011011", "0111010", "1101011", "1101111", "1010010", "1111111", "1111011"};
int get_int(string str) {
    int res = 0;
    for (int i = 6, j = 1; ~i; i--, j *= 2) if (str[i] == '1') res += j;
    return res; 
}

int main()
{   
    memset(dp, -1, sizeof(dp));
    memset(mp, -1, sizeof(mp));

    cin >> n >> k;

    for (int i = 0; i < n; i++) {
        string t;
        cin >> t;
        int x = get_int(t);
        for (int j = 0; j < 10; j++) {
            int y = get_int(s[j]);
            if ((x & y) == x) mp[i][__builtin_popcount(x ^ y)] = j;
        }
    }

    for (int i = 0; i < 8; i++) dp[n - 1][i] = mp[n - 1][i];

    for (int i = n - 2; ~i; i--) {
        for (int j = 0; j <= k; j++) if (dp[i + 1][j] >= 0){
            for (int p = 0; p <= 7 && j + p <= k; p++) dp[i][j + p] = max(dp[i][j + p],  mp[i][p]);
        }
    }

    int cnt = 0;
    for (int i = 0; i < n; i++) {
        int u = dp[i][k - cnt];
        cout << u;
        if (u == -1) return 0;
        for (int j = 0; j <= 7; j++) if (mp[i][j] == u) {
            cnt += j;
            break;
        }
    }
    cout << endl;

    return 0;
}

1338B - Edge Weight Assignment(好题:通过构造点权值来构造边权值)

给一颗树,为每条边赋一个权值(正整数),问最少和最多需要几个不同的正整数,使得任意一对叶子之间路径异或值为0,既有:\(l_1w_0v_1w_1v_2w_2...v_iw_il_2\)\(w_0 \bigoplus w_1 \bigoplus w_2 \bigoplus ... \bigoplus w_i = 0\)

题解

最少需要1个或者3个,也就是对所有的边赋值同一个整整数,分类讨论一下即可。

为每个叶子节点赋点权值:0,为非叶节点赋点权值:\(2^i\)。则边权值为:\(w(e(i,j)) = v_i \bigoplus v_j\)。显然,任意一对叶子之间的路径异或为0,所以最多需要\(e - \sum_v(leaf(v) - 1)\)个正整数。简直妙级了!

\(leaf(v)\)表示节点\(v\)的叶儿子个数.

1336A - Linova and Kingdom

给一颗\(n\)个节点的树,根为1,从树中选取\(k\)个节点建立工厂,剩余的所有节点建立公园,问这\(k\)个工厂各自派出一个工人,这些工人走到树根最多能游玩几个公园?工人只能沿着最短路走。

\(1 \leq k \leq n \leq 2e5\)

题解

当一个节点被选择建立工厂时,它的所有子节点在之前就已经被选择建立工厂,否则具有更优的方案。所以每个节点的实际贡献为\(dep[u] - size[u]\)。求出每个点的实际贡献,排个序,从大往小选择\(k\)个贡献较大的节点。

1333C - Eugene and an array

给一个序列,问有多少个子串满足:子串的任意一个子串都是好子串 ?

  • 好子串\(A_{l...r}\)\(a_l + a_{l +1} + a_{l + 2} + ... + a[r] \neq 0\)

\(1 \leq n \leq 2e5\)\(-10^9 \leq a_i \leq 10^9\)

题解

\(R[i]\)表示以\(i\)为左端点且最短 非好子串 的右端点位置,既\(a_i + a_{i + 1} + a_{i + 2} + ... + a_{R[i]} = 0\)。当然,也可能不存在\(R[i]\)。记\(L[R[i]] = i\),那么\(i\)位置为左端点的好子串个数就是:\(R[j] - i\) 或者是 \(n - i + 1\)\(j = (argmin_jR[j] \geq i) \&\& (L[R[j]] \geq i)\)\(j\) 的取值应该使得\(R[j]\)尽量靠近\(i\)的同时其左边界要大于等于\(i\)

怎么求\(R[i]\)?求出所有位置的前缀和\(sum[i]\),显然有\(sum[R[i]] = sum[i-1]\),用一个二维数组记录相同\(sum\)值出现的所有位置,然后二分查找满足等式且最小的\(R[i]\)就可以了。

posted @ 2021-06-29 23:06  天之道,利而不害  阅读(167)  评论(0编辑  收藏  举报