20230414 训练记录:前后缀

Posted Patricky

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20230414 训练记录:前后缀相关的知识,希望对你有一定的参考价值。

#删除一段的最大子段和 #删除两段的最大子段和 #前后缀
时间过得真快啊

去年这个时候打 ZUCC 校赛同步赛时还在牛客写了两个题解,里面有句话:

一年后的我已经学会了 lca,那现在算不算长大了呢?

这两篇题解也还挺有意思的,搬到这里好了!

Sum of Numerators

给定 \\(N, K\\),求解将序列 \\(\\left\\\\dfraci2^K\\right\\\\) 中元素全部约分后的分子和,其中 \\(i\\) 遍历 \\(1 \\sim N\\)

\\(N \\in [1, 10^9], K \\in [0, 10 ^ 9]\\)

首先注意到 \\(\\gcd(2^K, \\mathrmodd) = 1\\),于是我们先将所有奇数和算入答案,我们知道:

\\[\\sum_i = 1^N[i \\% 2 = 1] \\times i = \\bigg\\lceil\\dfracN2\\bigg\\rceil ^2 \\]

接下来考虑偶数与 \\(2 ^ K\\) 约分的过程,偶数可写作 \\(X = x2 ^ t,\\;\\mathttwhere\\;\\gcd(2, x) = 1\\)。于是 \\(2 ^ K\\) 将会约分所有 \\(t \\le K\\) 的部分,使得其变为一段奇数和,使用上述算式求解即可。

最后再加上没有被约分的偶数和即可,即

\\[\\displaystyle\\sum_i = 1^N[i \\% 2 = 0] \\times i = \\bigg\\lfloor\\dfracN2\\bigg\\rfloor \\times \\bigg(\\bigg\\lfloor\\dfracN2\\bigg\\rfloor + 1\\bigg) \\]

展开代码
using i64 = long long;

void solve() 
    int n, k;
    std::cin >> n >> k;
 
    i64 ans = 0;
    for (k += 1; k -- && n; n -= (n + 1) / 2) 
        ans += 1LL * ((n + 1) / 2) * ((n + 1) / 2);
        (!k) && (ans += 1LL * (n / 2) * (n / 2 + 1));
    
     
    std::cout << ans << \'\\n\';

Disjoint Path On Tree

给定一棵 \\(N\\) 个节点的树,求解二元组 \\((u, v)\\) 的个数,其中 \\(u, v\\) 是俩不相交的简单路径。
路径 \\((i, j)\\)\\((j, i), \\,i \\ne j\\) 视作同一路径。

\\(N \\in [1, 2 \\times 10 ^ 5]\\)

显然,树上的路径均为简单路径。容易知道 \\(N\\) 个节点的树的简单路径条数为:

\\[F(N) = N + \\displaystyle\\binomn2 = \\dfracN \\times (N + 1)2 \\]

\\((i, j)\\)\\((j, i), \\,i \\ne j\\) 视作一样的也没关系,求出所有二元组后除以二即可。

另一方面,所求也等价于 \\(\\Big((F(N) ^ 2 -\\) 相交组数 \\(\\Big)\\),设两条路径交于 \\(u\\),即选择

\\[((u, x)_cyc, (u, y)_cyc)_cyc \\]

讨论 \\(x, y\\) 的来源:

  1. \\(x \\in u\\),即 \\(\\mathrmcnt_1 = F(u) - \\displaystyle \\sum_to \\;\\in\\;u F(to)\\)
  2. \\(x \\notin u\\),即 \\(\\mathrmcnt_2 = \\mathrmsize_u \\times (n - \\mathrmsize_u)\\)

注意不能是两点均来自 \\(x \\notin u\\)。组合起来:

\\[(\\mathrmcnt_1 + \\mathrmcnt_2) ^ 2 - (\\mathrmcnt_2)^2 \\]

展开代码
Z f(int n)  return 1LL * n * (n + 1) / 2; 

void solve() 
  int n;
  std::cin >> n;

  std::vector<std::vector<int>> G(n);

  for (int i = 1, u, v; i < n; ++i) 
    std::cin >> u >> v;
    -- u, -- v;
    G[u].push_back(v);
    G[v].push_back(u);
  

  Z ans = f(n) * f(n);
  std::vector<int> size(n);

  std::function<void(int, int)> dfs = [&](int u, int p) 
    size[u] = 1;
    for (auto &&to : G[u]) if (to != p) 
      dfs(to, u);
      size[u] += size[to];
    

    Z cnt1 = f(size[u]);
    Z cnt2 = 1LL * size[u] * (n - size[u]);

    for (auto &&to : G[u]) if (to != p) 
      cnt1 -= f(size[to]);
    

    ans -= (cnt1 + cnt2) * (cnt1 + cnt2) - cnt2 * cnt2;
  ;

  dfs(0, -1);

  std::cout << ans / 2 << \'\\n\';

至多删除一/两段的最大子段和

集训队小伙伴问的面试题,挺有意思的。中途还断断续续地去请教了 tarjen 大佬,感激不尽。

至多一段

注意到,最终的答案是两段拼起来的:枚举 \\(i\\),求出 \\(i\\) 左侧的最大子段和、右侧的最大子段和,作为候选答案。实际上问题转换为求出前缀、后缀的最大子段和的最大值。这种前后缀拼起来贡献答案的题挺常见的。

展开代码
#include <bits/stdc++.h>

int main() 
    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n;
    std::cin >> n;
    std::vector<int> a(n + 1), fp(n + 1), fs(n + 2);
    for (int i = 1; i <= n; i++) std::cin >> a[i];
    for (int i = 1; i <= n; i++) fp[i] = std::max(fp[i - 1], 0) + a[i];
    for (int i = 1; i <= n; i++) fp[i] = std::max(fp[i], fp[i - 1]);
    for (int i = n; i >= 1; i--) fs[i] = std::max(fs[i + 1], 0) + a[i];
    for (int i = n; i >= 1; i--) fs[i] = std::max(fs[i], fs[i + 1]);

    int ans = 0;
    for (int i = 1; i <= n; i++) 
        ans = std::max(ans, fp[i - 1] + fs[i + 1]);
    
    
    std::cout << std::max(ans, fp[n]) << \'\\n\';
    
    return 0;

至多两段

我首先想的是,是否等价于做两次上面的那个问题。然后被群友 Hack 了:

这时候 tarjen 好哥哥说直接 \\(\\mathcal O(n \\log n)\\) 扫一遍就好了,我却没有理解到,接着加好友学了一波。答案实际上也是两段,不过这次是前区间和最小的两段,问题转换为求出所有前缀中,区间和最小的一段和为多少。区间和是两段前缀和之差。即对于当前遍历到的 \\(i\\),前缀和为 \\(s_i\\),找出所有前缀和 \\(s_j\\,(j \\lt i)\\) 中的最大值,和 \\(s_i\\) 的差就是前缀中最小的那段区间和了。

举一些例子来描述这个过程:
-1 -1 -1 -1 -1 [-1]

当前遍历到的前缀和为 \\(-6\\),前缀和集合为 \\(\\colorred0\\) \\(-1, -2, -3, -4, -5\\) 最大的是 \\(0\\),因此当前最小区间和为 \\(-6\\)

1 2 3 -6 5 [-6]

当前遍历到的前缀和为 \\(-4\\),前缀和集合为 \\(0, 1, 3, 5, 6\\),所以求出最小的区间和为 \\(-1 - 6 = -7\\)

展开代码
#include <bits/stdc++.h>

using ll = long long;

int main() 
    std::cin.tie(nullptr)->sync_with_stdio(false);

    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    std::vector<ll> ps(n + 1), ss(n + 2);
    for (int i = 1; i <= n; i++) std::cin >> a[i];
    for (int i = 1; i <= n; i++) ps[i] = ps[i - 1] + a[i];
    for (int i = n; i >= 1; i--) ss[i] = ss[i + 1] + a[i];

    std::set<ll, std::greater<ll>> s;

    constexpr ll inf = 1e18;
    std::vector<ll> fp(n + 1), fs(n + 2);
    fp[0] = fs[n + 1] = inf;
    for (int i = 1; i <= n; i++) 
        s.insert(ps[i - 1]);
        fp[i] = std::min(fp[i - 1], ps[i] - *s.begin());
    
    s.clear();
    for (int i = n; i >= 1; i--) 
        s.insert(ss[i + 1]);
        fs[i] = std::min(fs[i - 1], ss[i] - *s.begin());
    

    fp[0] = fs[n + 1] = 0;

    ll ans = 0;
    for (int i = 1; i <= n; i++) 
        ans = std::min(ans, fp[i - 1] + fs[i + 1]);
    

    std::cout << std::max<ll>(0, ps[n] - ans) << \'\\n\';

    return 0;

学弟一天就写了个单调队列模板题然后十分得意,想说一通可是基本功实力不如他,讨厌自己。

Dailight 训练记录

现场赛记录

19 CCPC 湘潭邀请赛  11/Gold

19 ICPC 西安邀请赛 49/Silver

 

训练规划:

hl:

1.深入增强图论,数据结构的能力,包括但不限于树形dp,点分治,多写dp,学习斜率优化,四边形优化

2.保证银牌及以下图论,数据结构,dp的通过率,最好可以在十分钟内出思路

3.养成提交前检查代码的习惯,不出现傻逼错误。

4.学习简单数论(逆元,组合数),尤其是数论结合图论的应用

 

gbs:

1.加快上机 ->  写完代码过样例 这一过程的速度。

2.减少debug占用机时

3.首要任务是增加银牌及以下数论的通过率,包括但不限于打表找规律,基础推公式,其次学习较难的数论。

4.增加用简要语言表述题意的能力

 

zcz.

1.训练计算几何的能力,保证简单及中等计算几何的通过率

2.多看一些思维题及找规律,推公式,构造题,写代码为次,提出正确思路为主。

3.写题的过程尽量了解ACM的出题套路。

 

总:

1.所有队员多做思维题,保证脑子灵活度

2.多开训练赛,增加讨论题目的配合能力以及队伍机时调度的能力

 

经验总结:

1.交之前务必重新审视两边代码保证正确,WA之后直接下机并且打印代码,2WA之后需要队友确认题意并且检查代码

2.签到题签完之后如果过的人数中等且写题的十分自信,可以本题单线程,否则需要其他至少一位队友确认题意并且确认写法才可敲题

3.开局开荒题的队员除非特别有自信且剩余码量不高,否则优先将机子让给队友写签到题以降低罚时

4.非最后十分钟垃圾时间严禁挂机,当前没有题挂在线程上的队员就去读没有读过的题

5.难题采用数论给gbs和zcz讨论,图论数据结构给hl,计算几何给zcz,无法确定类型的读给hl确认类型的战略,确认题是否可做

 

训练赛记录

The 10th Shandong Provincial Collegiate Programming Contest   11/13     rak3/249

以上是关于20230414 训练记录:前后缀的主要内容,如果未能解决你的问题,请参考以下文章

20230414小记

总结20230414

java学习日记20230414-Set接口

NOI前训练记录

编程训练-考研上机模拟综合模拟2-2019浙大上机模拟(晴神)

省选前训练日记