友谊赛题解

本文详细解析了四道算法题目,涉及邮递问题的不同解决方案,包括 O(1)、O(nlogn) 等复杂度的算法;回文字符串的构造方法,包括 O(nlogn)、O(nα(n)) 等算法;求和问题的高效算法,如利用 σ 函数和等比数列进行计算;以及染色问题的贪心策略和线段树优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A-Rainyrabbit 爱邮递

算法 1

每次重建快递站后直接深搜一遍累加贡献就好了,然后 O ( 1 ) \mathcal{O}(1) O(1) 回答。

你可以获得 10pts 的好成绩。

算法 2

菊花图,这个很简单啊。

如果加的是花心,相当于其他城市都加 1 1 1,否则就是花心的城市加 1 1 1,其他城市加 2 2 2

结合算法 1 可以获得 30pts 的好成绩。

算法 3

一条链的话也是送的,直接拆拆贡献用棵线段树维护即可。

结合算法 1,2 可以获得 60pts 的好成绩。

算法 4

做法 1

考虑树怎么做,很套路的可以将 d i s ( x , y ) dis(x,y) dis(x,y) 拆成 d e p x + d e p y − 2 d e p lca ⁡ ( x , y ) dep_x+dep_y-2dep_{\operatorname{lca}(x,y)} depx+depy2deplca(x,y),但是拆了还是不好做,修改最坏还是 O ( n ) \mathcal{O(n)} O(n) 的。

发现 d e p lca ⁡ ( x , y ) dep_{\operatorname{lca}(x,y)} deplca(x,y) 这个玩意可以借助 [LNOI2014]LCA 的思想,将树根 1 1 1 x x x 上的边都加上一次这条边的权值,然后节点 y y y 往上爬,途中边排边累加该边的贡献,爬到根后就是 d e p lca ⁡ ( x , y ) dep_{\operatorname{lca}(x,y)} deplca(x,y) 的值。

于是便有了一个新的算法,对于重建快递站到 u u u,直接将 u u u 往上爬,并更新每条边新的贡献,查询的话就跟刚才一样的往上爬就好了。

暴力往上爬的话可以过数据随机的点,然后结合算法 1,2,3 就能获得 80pts 的好成绩。

正解直接用树链剖分优化即可。

时间复杂度 O ( n log ⁡ 2 n ) \mathcal{O}(n\log^2 n) O(nlog2n)

#include <cstdio>
#include <cstring>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define int long long
int n, m, totdep, tot, a[200005], Dep[200005], fa[200005], size[200005], dep[200005], son[200005],
    seg[200005], rev[200005], top[200005];
struct node {
   
    int to, w;
};
vector<node> G[200005];
void dfs1(int u, int f) {
   
    size[u] = 1, dep[u] = dep[f] + 1, fa[u] = f;
    for (int i = 0; i < G[u].size(); i++) {
   
        int v = G[u][i].to;
        if (v == f)
            continue;
        Dep[v] = Dep[u] + G[u][i].w;
        a[v] = G[u][i].w;
        dfs1(v, u);
        size[u] += size[v];
        if (size[v] > size[son[u]])
            son[u] = v;
    }
}
void dfs2(int u, int f) {
   
    if (son[u]) {
   
        seg[son[u]] = ++seg[0];
        top[son[u]] = top[u];
        rev[seg[0]] = son[u];
        dfs2(son[u], u);
    }
    for (int i = 0; i < G[u].size(); i++) {
   
        int v = G[u][i].to;
        if (v == f)
            continue;
        if (!top[v]) {
   
            seg[v] = ++seg[0];
            top[v] = v;
            rev[seg[0]] = v;
            dfs2(v, u);
        }
    }
}
struct Segment_Tree {
   
    struct node {
   
        LL xsum, sum, lazy;
    };
    node tree[800005];
#define lson(x) x << 1
#define rson(x) x << 1 | 1
    void build(int x, int l, int r) {
   
        if (l == r) {
   
            tree[x].sum = a[rev[l]];
            return;
        }
        int mid = (l + r) >> 1;
        build(lson(x), l, mid);
        build(rson(x), mid + 1, r);
        tree[x].sum = tree[lson(x)].sum + tree[rson(x)].sum;
        tree[x].xsum = tree[lson(x)].xsum + tree[rson(x)].xsum;
    }
    void push_down(int x) {
   
        if (tree[x].lazy) {
   
            tree[lson(x)].lazy += tree[x].lazy;
            tree[rson(x)].lazy += tree[x].lazy;
            tree[lson(x)].xsum += tree[x].lazy * tree[lson(x)].sum;
            tree[rson(x)].xsum += tree[x].lazy * tree[rson(x)].sum;
            tree[x].lazy = 0;
        }
    }
    void add(int x, int l, int r, int L, int R) {
   
        if (L <= l && r <= R) {
   
            tree[x].lazy++;
            tree[x].xsum += tree[x].sum;
            return;
        }
        push_down(x);
        int mid = (l + r) >> 1;
        if (L <= mid)
            add(lson(x), l, mid, L, R);
        if (mid + 1 <= R)
            add(rson(x), mid + 1, r, L, R);
        tree[x].xsum = tree[lson(x)].xsum + tree[rson(x)].xsum;
    }
    LL query(int x, int l, int r, int L, int R) {
   
        if (L <= l && r <= R)
            return tree[x].xsum;
        LL ans = 0;
        int mid = (l + r) >> 1;
        push_down(x);
        if (L <= mid)
            ans += query(lson(x), l, mid, L, R);
        if (mid + 1 <= R)
            ans += query(rson(x), mid + 1, r, L, R);
        return ans;
    }
} T;
void change(int u) {
   
    while (u) {
   
        int fu = top[u];
        T.add(1, 1, n, seg[fu], seg[u]);
        u = fa[fu];
    }
}
LL query(int u) {
   
    LL ans = 0;
    while (u) {
   
        int fu = top[u];
        ans += T.query(1, 1, n, seg[fu], seg[u]);
        u = fa[fu];
    }
    return ans;
}
signed main() {
   
    scanf("%lld %lld", &n, &m);
    for (int i = 1; i < n; i++) {
   
        int u, v, w;
        scanf("%lld %lld %lld", &u, &v, &w);
        G[u].push_back(node{
    v, w });
        G[v].push_back(node{
    u, w });
    }
    dfs1(1, 0);
    seg[0] = 1, seg[1] = 1, top[1] = rev[1] = 1;
    dfs2(1, 0);
    T.build(1, 1, seg[0]);
    while (m--) {
   
        int opt, u;
        scanf("%lld %lld", &opt, &u);
        if (opt == 1)
            change(u), totdep += Dep[u], tot++;
        else
            printf("%lld\n", totdep + 1ll * tot * Dep[u] - 2 * query(u));
    }
    return 0;
}

做法 2

来自巨佬 WY 的做法,cdq 分治+虚树。

B-Rainyrabbit 爱回文

很遗憾,因为数据过水,导致有 5 人 AC,但是本质上只有 3 人写的正解,其他的时间复杂度都是错的。

多次询问一个字符串 s s s,问构造若干字符串 t 1 , t 2 , ⋯   , t k t_1,t_2,\cdots ,t_k t1,t2,,tk 使得 s = t 1 t 2 ⋯ t k s=t_1t_2\cdots t_k s=t1t2tk 并且 ∀ i ∈ [ 1 , k ] , t i ≥ 2 \forall i \in [1,k],t_i \geq 2 i[1,k],ti2 并且是回文串的方式是否存在。 T ≤ 10 , ∣ s ∣ ≤ 1 0 6 T \leq 10,|s| \leq 10^6 T10,s

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值