HOJ1127听风

Posted shiina-rikka

tags:

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

HOJ1127听风

该网页无法正常运作

题面(题面难懂这就是语文题

一棵根节点为1的树,所有边权均为1,每个节点有不同的颜色C。对这棵树进行游览并统计贡献。

游览方式
1.如果一个节点被游览过就不再游览。
2.如果在i号节点,它是叶子节点或者所有儿子都被游览过则停在该节点。
3.如果第 i号节点存在儿子没有游览过,则挑选所有未游览的儿子中val[x]最小的一个继续行走。val[x] = min{y|y${in}$x的子树}。
可以任意选择在游览几个节点后结束游览

每游览一个节点,贡献会加上 这个节点到根节点以及它的子树中颜色的种类 $-$ 这个节点到根节点的路径长度

游览结束后,会给很多路径

贡献会加上max(每条路径上被游览的点数 $-1$,$0$)$*$题目提供的权值z
最终答案为游览结束后的所有可能贡献中的最大值

这题该咋做

首先dfs求出游览的顺序,然后树上差分,把最后路径的权值扔到节点上,最后按照游览顺序遍历这颗树,统计当前的总贡献,与答案取max。
至于节点的颜色,因为数据比较小bitset状态压缩就可以过。std给的是dsu on tree,等我学会了再来更新。

涉及的知识点

LCA

LCA
这题我用倍增毕竟Tarjan不会

树上差分

其实是第一次写树上差分
这道题在处理路径的时候使用树上差分。但是给的是路径上节点数-1,该怎么处理呢?
由于游览顺序已知,我们只要不记录这条路径上最先被游览的节点,就可以保证在计算这条路径时有-1的效果。
树上差分的标记是打在左右两个节点的,我们只要挑选出这两个中先被游览的点,把标记打在他的父亲即可。

dsu on tree

不会·咕咕咕一个好题

代码

这个是bitset的代码,极慢,卡着空间。。。

#include <iostream>
#include <cstdio>
#include <bitset>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <ctime>
namespace fdata
{
inline char nextchar()
{
    static const int BS = 1 << 21;
    static char buf[BS], *st, *ed;
    if (st == ed)
        ed = buf + fread(st = buf, 1, BS, stdin);
    return st == ed ? -1 : *st++;
}
template <typename T>
inline T poread()
{
    T ret = 0;
    char ch;
    while (!isdigit(ch = nextchar()))
        ;

    do
        ret = ret * 10 + ch - '0';
    while (isdigit(ch = nextchar()));
    return ret;
}
} // namespace fdata
using fdata::poread;
using namespace std;
const int INF = 1 << 29;
const int MAXN = 1e5 + 5;
const int MAXC = 4e3 + 7;
int n, m, c, t;
int head[MAXN], ver[MAXN << 1], nxt[MAXN << 1], tot; //邻接表
inline void add(const int &x, const int &y)
{
    ver[++tot] = y;
    nxt[tot] = head[x];
    head[x] = tot;
}
struct node //记录节点信息
{
    bitset<MAXC> lian, c;
    vector<int> vct;
    int deep;
    int son;
    int siz;
} pt[MAXN];
int dfn[MAXN], cnt;
int f[MAXN][19]; // lca
bool cmp(const int &x, const int &y)
{
    return pt[x].son < pt[y].son;
}
void dfs1(int x) //处理子树集合,父亲节点
{
    pt[x].son = x;
    pt[x].lian |= pt[f[x][0]].lian;
    for (register int i = head[x], y = ver[i]; i; i = nxt[i], y = ver[i])
    {
        if (y == f[x][0])
            continue;
        dfs1(y);
        pt[x].c |= pt[y].c;
        pt[x].son = min(pt[x].son, pt[y].son);
    }
}
void dfs2(int x)
{
    for (register int i = head[x]; i; i = nxt[i])
        if (ver[i] != f[x][0])
            pt[x].vct.push_back(ver[i]);
    sort(pt[x].vct.begin(), pt[x].vct.end(), cmp);
    for (register vector<int>::iterator it = pt[x].vct.begin(); it != pt[x].vct.end(); ++it)
        dfs2(*it);
    dfn[x] = ++cnt;
    pt[x].vct.clear();
}
int lca(int x, int y)
{
    if (pt[x].deep > pt[y].deep)
        swap(x, y);
    int t = (int)(log(n) / log(2)) + 1;
    for (register int i = t; i >= 0; --i)
    {
        if (pt[f[y][i]].deep >= pt[x].deep)
            y = f[y][i];
    }
    if (y == x)
        return x;
    for (register int i = t; i >= 0; --i)
        if (f[x][i] != f[y][i])
            x = f[x][i], y = f[y][i];
    return f[x][0];
}
long long Ans = 0;
long long ans = 0;
void dfs3(int x)
{
    for (register int i = head[x]; i; i = nxt[i])
    {
        int y = ver[i];
        if (y == f[x][0])
            continue;
        dfs3(y);
        pt[x].siz += pt[y].siz;
    }
}
void dfs4(int x)
{
    for (register int i = head[x]; i; i = nxt[i])
        if (ver[i] != f[x][0])
            pt[x].vct.push_back(ver[i]);
    sort(pt[x].vct.begin(), pt[x].vct.end(), cmp);
    for (register vector<int>::iterator it = pt[x].vct.begin(); it != pt[x].vct.end(); ++it)
        dfs4(*it);
    ans = (long long)ans + (1ll * pt[x].lian.count() - pt[x].deep + 1) + (1ll * pt[x].siz);
    Ans = max(ans, Ans);
    pt[x].vct.clear();
}
inline void bfs()
{
    queue<int> q;
    q.push(1);
    pt[1].deep = 1;
    f[1][0] = -1;
    while (q.size())
    {
        int x = q.front();
        q.pop();
        for (register int i = head[x], y = ver[i]; i; i = nxt[i], y = ver[i])
        {
            if (pt[y].deep)
                continue;
            pt[y].deep = pt[x].deep + 1;
            f[y][0] = x;
            for (register int j = 1; j <= t; ++j)
            {
                f[y][j] = f[f[y][j - 1]][j - 1];
            }
            q.push(y);
        }
    }
    while (q.size())
        q.pop();
}
bitset<MAXC> iii;
signed main()
{
#ifdef lky233
    freopen("testdata.in", "r", stdin);
    freopen("testdata.out", "w", stdout);
#endif
    n = poread<int>(), m = poread<int>(), c = poread<int>();
    t = log(n) / log(2) + 1;
    for (register int i = 1; i <= n; ++i)
        pt[i].lian[poread<int>()] = true, pt[i].c = pt[i].lian;
    for (register int i = 1, x, y; i < n; ++i)
    {
        x = poread<int>(), y = poread<int>();
        add(x, y), add(y, x);
    }
    bfs();
    dfs1(1);
    for (register int i = 1; i <= n; ++i)
        pt[i].lian = pt[i].c | pt[i].lian;
    // for (register int i = 1; i <= n; ++i)
    //     cerr << pt[i].lian.count() << " ";
    // cerr << endl;
    dfs2(1);
    for (register int i = 1, x, y, z; i <= m; ++i)
    {
        x = poread<int>(), y = poread<int>(), z = poread<int>();
        if (x == y)
            continue;
        if (dfn[x] >= dfn[y])
            swap(x, y);
        x = f[x][0];
        if (x)
            pt[x].siz += z;
        if (y)
            pt[y].siz += z;
        int L = lca(x, y);
        if (L)
            pt[L].siz -= z;
        if (f[L][0])
            pt[f[L][0]].siz -= z;
        // cerr << x << y << L << f[L][0] << endl;
    }
    dfs3(1);
    dfs4(1);
    cerr << "ans: ";
    cerr << Ans << endl;
    cout << Ans << endl;
    cerr << clock() << "ms" << endl;
    return 0;
}

至于 dsu on tree 回头更新

坑点

在树上差分标记的时候,给了一个1-1的路径,这个时候父亲跳到了上面,0有了+3-3-3的标记,1有了+3的标记,查询时导致少了-3,挂掉了。

技术图片

持续更新ing

感谢nofind大佬解释题面,教我树上差分
ありがとうございました。







以上是关于HOJ1127听风的主要内容,如果未能解决你的问题,请参考以下文章

hoj 13792 Model Railroad

技术思路极客时间-左耳听风-开篇词2

c_cpp HOJ - 302:最大平均值[AC]; http://hoj.twbbs.org/judge/judge/submission/21872

HOJ——T 1867 经理的烦恼

HOJ 1006

HOJ——T 2430 Counting the algorithms