九月题目泛做总结。

Keywords:

LCT,Dynamic D&C Tree,Greedy,Smart Mind

Table of Contents

  1. Keywords:
  2. Overview
  3. Luogu P3613 睡觉困难综合征
  4. BZOJ 4012 开店
    1. Description
    2. Solution
  • BZOJ 1134 Maf
    1. Description
  • Overview

    最近做的题太多了,又懒得写一题就放一篇题解,但是这样还是看起来工作量很大,计划一篇放大概五道题左右吧。

    这样既不麻烦又可以冲文章数量的说

    Luogu P3613 睡觉困难综合征

    前身是起床困难综合征.

    所以这也是一道按位贪心题. 但是上树,多次查询, 带修改了.

    那么就要支持快速合并信息和修改信息了.

    因为位运算不支持换位, 所以我们要考虑特别合并两个方向的答案.

    这样分别考虑不同操作的值影响就可以上LCT了. 合并和点权构造见代码.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<algorithm>
    #include<bitset>
    #include<cctype>

    using namespace std;

    typedef unsigned long long ull;

    const int maxn = 1e+5 + 5;
    const ull one = 1;
    const ull zr = 0;

    struct edge
    {
    int to, nxt;
    }e[maxn << 1];
    int ptr, lnk[maxn];

    struct val
    {
    ull f0, f1;
    inline val operator + (const val &rhs) const
    {
    val ret;
    ret.f0 = (~f0&rhs.f0)|(f0&rhs.f1);
    ret.f1 = (~f1&rhs.f0)|(f1&rhs.f1);
    return ret;
    }
    };
    struct node
    {
    val f, l, r;
    int rev, c[2];
    }t[maxn];
    int n, m, k, u, v, f[maxn];
    ull num;

    inline int rd()
    {
    register int x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }
    inline ull rdll()
    {
    register ull x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }

    inline void add(int bgn, int end)
    {
    e[++ptr] = (edge){end, lnk[bgn]}; lnk[bgn] = ptr;
    }

    #define ls t[x].c[0]
    #define rs t[x].c[1]
    inline bool isroot(int x) {return t[f[x]].c[0] != x && t[f[x]].c[1] != x; }
    inline void pushup(int x)
    {
    t[x].l = t[x].r = t[x].f;
    if(ls) t[x].l = t[ls].l + t[x].l, t[x].r = t[x].r + t[ls].r;
    if(rs) t[x].l = t[x].l + t[rs].l, t[x].r = t[rs].r + t[x].r;
    }
    inline void pushnode(int x) { swap(ls, rs); swap(t[x].l, t[x].r); t[x].rev ^= 1; }
    inline void pushdown(int x) { if(t[x].rev && x) pushnode(ls), pushnode(rs), t[x].rev = 0; }
    inline int getid(int x) { return t[f[x]].c[1] == x; }
    inline void rotate(int x)
    {
    int fa = f[x], fa_ = f[fa], k = getid(x);
    if(!isroot(fa))
    t[fa_].c[t[fa_].c[1] == fa] = x;
    t[fa].c[k] = t[x].c[k ^ 1];
    f[t[fa].c[k]] = fa;
    t[x].c[k ^ 1] = fa;
    f[fa] = x; f[x] = fa_;
    pushup(fa); pushup(x);
    }
    void pushpath(int x)
    {
    if(!isroot(x)) pushpath(f[x]);
    pushdown(x);
    }
    inline void splay(int x)
    {
    pushpath(x);
    for(int fa; !isroot(x); rotate(x))
    {
    if(!isroot(fa = f[x]))
    rotate(getid(x) == getid(fa) ? fa : x);
    pushup(x);
    }
    }
    inline void access(int x) { for(int y = 0; x; y = x, x = f[x]) splay(x), rs = y, pushup(x); }
    inline void makeroot(int x) { access(x), splay(x), pushnode(x); }
    inline void split(int x, int y) { makeroot(x); access(y); splay(y); }
    inline void dfs(int x)
    {
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(y == f[x]) continue;
    f[y] = x, dfs(y);
    }
    pushup(x);
    }

    int main()
    {
    n = rd(); m = rd(); k = rd();
    for(int i = 1; i <= n; ++i)
    {
    u = rd(); num = rdll();
    if(u == 1) t[i].f = (val){zr, num};
    else if(u == 2) t[i].f = (val){num, ~zr};
    else t[i].f = (val){num, ~num};
    }
    for(int i = 1; i < n; ++i)
    {
    u = rd(); v = rd();
    add(u, v); add(v, u);
    }
    dfs(1);
    //puts("debug");
    int opt;
    while(m--)
    {
    opt = rd(); u = rd(); v = rd(); num = rdll();
    if(opt == 2)
    {
    if(v == 1)
    t[u].f = (val){zr, num};
    else if(v == 2)
    t[u].f = (val){num, ~zr};
    else t[u].f = (val){num, ~num};
    splay(u);
    }
    else
    {
    split(u, v); ull ans = 0;
    for(int i = k; i; --i)
    {
    if(t[v].l.f0 & (one << (i-1))) ans |= (one << (i-1));
    else if((t[v].l.f1 & (one << (i-1))) && num >= (one << (i-1)))
    ans |= (one << (i-1)), num ^= (one << (i-1));
    }
    printf("%llu\n", ans);
    }
    }
    return 0;
    }

    BZOJ 4012 开店

    Description

    树上每个点有点权, 对于一个点, 计算所有点权在$[L, R]$之间的点到她的距离.

    Solution

    • Step 1

      计算所有点到一个点的距离.

    • Step 2

      二分出特定点权范围的位置, 前/后缀和处理一下.

    首先是一套点分啦.

    然后暴力向下计算距离和子树大小, 对于每个点每个方向做分治($[0,2]$), 记录祖先, 因为分治, 这些暴力都是$O(logn)$级别的.

    有祖先和子树下面的答案, 就可以开始统计了, 子树大小用来作为系数, 统计多路贡献经过当前点和祖先之间路径的答案.

    STL提供的lower_bound对于前缀和不太友好, 所以我们改用后缀和. 这真是黑科技啊.

    操作见代码啦.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    #include <cstdio>
    #include <vector>
    #include <cctype>
    #include <bitset>

    using namespace std;

    typedef long long ll;

    const int maxn = 1e+5 + 5e+4 + 5;

    struct edge
    {
    int to, nxt, v;
    }e[maxn << 1];
    struct anc
    {
    ll dis; int pnt, id;
    anc(ll d_ = 0, int p_ = 0, int i_ = 0):
    dis(d_), pnt(p_), id(i_){}
    }; vector<anc> node_anc[maxn];
    struct dat
    {
    int age; ll sumdis, sumsiz;
    dat(int w = 0, ll d_ = 0, ll sz = 0):
    age(w), sumdis(d_), sumsiz(sz){}
    bool operator < (const dat &rhs) const
    {
    return age < rhs.age;
    }
    }; vector<dat> ans[maxn][3];

    typedef vector<dat>::iterator iter;

    int ptr, lnk[maxn], siz[maxn], a[maxn], sum, glbmn, rt;
    int n, q; ll A, lstans;
    ll dep[maxn];
    bitset<maxn> vis, cut;

    inline ll rdll()
    {
    register ll x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }
    inline int rd()
    {
    register int x = 0; char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) x = x * 10 + (c ^ 48), c = getchar();
    return x;
    }
    inline void add(int bgn, int end, int val)
    {
    e[++ptr] = (edge){end, lnk[bgn], val};
    lnk[bgn] = ptr;
    }
    void dfs(int x)
    {
    //puts("dfs");
    vis[x] = 1; siz[x] = 1;
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(!vis[y] && !cut[y]) dfs(y), siz[x] += siz[y];
    }
    vis[x] = 0;
    }
    void efs(int x)
    {
    //puts("efs");
    vis[x] = 1; int mx = sum - siz[x];
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(!vis[y] && !cut[y]) efs(y), mx = max(mx, siz[y]);
    }
    vis[x] = 0; if(mx < glbmn) glbmn = mx, rt = x;
    }
    void ffs(int x, const int &ast, const int &type_)
    {
    //puts("ffs");
    vis[x] = 1; node_anc[x].push_back(anc(dep[x], ast, type_));
    ans[ast][type_].push_back(dat(a[x], dep[x], 1));
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(!vis[y] && !cut[y]) dep[y] = dep[x] + e[p].v, ffs(y, ast, type_);
    }
    vis[x] = 0;
    }
    void conquer(int x)
    {
    //puts("conq");
    dfs(x);
    if(siz[x] == 1)//leaf
    {
    cut[x] = 1; node_anc[x].push_back(anc(0, x, -1));
    return;
    }
    sum = siz[x]; glbmn = 0x3f3f3f3f; efs(x);
    cut[rt] = 1; node_anc[rt].push_back(anc(0, rt, -1));
    int t = 0;
    for(int p = lnk[rt]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(cut[y]) continue;
    dep[y] = e[p].v; ffs(y, rt, t);
    ans[rt][t].push_back(dat(0x3f3f3f3f, 0, 0));
    sort(ans[rt][t].begin(), ans[rt][t].end());
    for(int j = ans[rt][t].size() - 2; j >= 0; --j)
    {
    ans[rt][t][j].sumdis += ans[rt][t][j+1].sumdis;
    ans[rt][t][j].sumsiz += ans[rt][t][j+1].sumsiz;
    }
    t++;
    }
    for(int p = lnk[rt]; p; p = e[p].nxt)
    if(!cut[e[p].to]) conquer(e[p].to);
    }
    void query(int l, int r, int u)
    {
    lstans = 0; iter L, R;
    for(int i = node_anc[u].size()-1; i >= 0; --i)
    {
    //puts("gett");
    int f = node_anc[u][i].pnt;
    for(int type_ = 0; type_ < 3; ++type_)
    {
    if(type_ == node_anc[u][i].id || ans[f][type_].empty()) continue;
    L = lower_bound(ans[f][type_].begin(), ans[f][type_].end(), dat(l, 0, 0));
    R = upper_bound(ans[f][type_].begin(), ans[f][type_].end(), dat(r, 0, 0));
    lstans += (*L).sumdis - (*R).sumdis + node_anc[u][i].dis * (L->sumsiz - R->sumsiz);
    }
    if(l <= a[f] && a[f] <= r) lstans += node_anc[u][i].dis;
    }
    }

    int main()
    {
    scanf("%d%d%lld", &n, &q, &A);
    for(int i = 1; i <= n; ++i) a[i] = rd();
    register int x, y, z;
    for(int i = 1; i < n; ++i)
    x = rd(), y = rd(), z = rd(), add(x, y, z), add(y, x, z);
    conquer(1);
    register int u; register ll l, r;
    //puts("q");
    while(q--)
    {
    u = rd(); l = rdll(); r = rdll();
    l = (l + lstans) % A; r = (r + lstans) % A;
    if(l > r) swap(l, r);
    query(l, r, u);
    printf("%lld\n", lstans);
    }
    return 0;
    }

    BZOJ 1134 Maf

    Description

    每个人会向一个人开枪, 可以是自己. 决定开枪顺序, 最大化和最小化死亡数目.

    最大化比较好想.

    自环的话, 就+1.

    单环的话, $siz - 1$.

    对于树, 除了叶子都可以被攻击到.

    统计答案就好.

    下面是最小化.

    来自学长讲课:

    叶子一定不能死, 丢进队列里. 这样每个人杀能杀的人, 那么目标的目标当前就能活下来. 最后会剩一些环, 除以2上取整.

    确实是很巧妙的思路呢.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    #include <bits/stdc++.h>

    using namespace std;

    const int maxn = 1e+6 + 5;

    struct edge
    {
    int to, nxt;
    }e[maxn];
    int n, a[maxn], bl[maxn], num, ptr, lnk[maxn], dgr2[maxn];
    int sz[maxn], vis[maxn], dgr[maxn], tot, lff;
    int gg[maxn];
    queue<int> q;

    inline void add(int bgn, int end)
    {
    e[++ptr].to = end; e[ptr].nxt = lnk[bgn]; lnk[bgn] = ptr;
    }
    void dfs(int x)
    {
    /*vis[x] = 1; tot++;
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(!vis[y]) dfs(y);
    }*/
    while(!vis[x])
    {
    tot++; vis[x] = 1;
    x = a[x];
    }
    }
    void efs(int x)
    {
    /*bl[x] = num; sz[num]++; vis[x] = 1;
    for(int p = lnk[x]; p; p = e[p].nxt)
    {
    int y = e[p].to;
    if(!vis[y]) efs(y);
    }*/
    while(!vis[x])
    {
    bl[x] = num; sz[num]++; vis[x] = 1;
    x = a[x];
    }
    }
    void gfs(int x)
    {
    while(!vis[x])
    {
    bl[x] = num; sz[num]++; vis[x] = 1;
    x = a[x];
    }
    }

    int main()
    {
    //("6.in", "r", stdin);
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
    scanf("%d", &a[i]), dgr2[a[i]]++, dgr[a[i]]++, add(i, a[i]);
    for(int i = 1; i <= n; ++i)
    if(!dgr[i]) dfs(i), lff++;
    for(int i = 1; i <= n; ++i)
    if(!vis[i])
    {
    ++num;
    efs(i);
    }
    for(int i = 1; i <= num; ++i)
    {
    if(sz[i] == 1) tot++;
    else tot += sz[i] - 1;
    }
    tot -= lff;
    int ans2 = tot;
    //printf("%d ", tot);
    tot = 0;
    fill(sz, sz + 2 + n, 0);
    fill(vis, vis + 2 + n, 0);
    num = 0;
    for(int i = 1; i <= n; ++i)
    if(!dgr2[i]) q.push(i);
    while(!q.empty())
    {
    int u = q.front(); q.pop();
    vis[u] = 1;
    int tar = a[u], t_ = a[tar];
    if(!gg[tar])
    {
    vis[tar] = gg[tar] = 1; tot++;
    --dgr2[t_];
    if(!dgr2[t_]) q.push(t_);
    }

    }
    for(int i = 1; i <= n; ++i)
    if(!vis[i])
    {
    ++num;
    gfs(i);
    tot += (sz[num] + 1) / 2;
    }
    int ans1 = tot;
    printf("%d %d", ans1, ans2);
    return 0;
    }

    里面留了调试痕迹, 作为提醒咯.


    Bouvardia偷懒不想写啦, 后面做了很多网络流, 在网络流总结里.

    那么, 第一篇就到这里.