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

一个好题
这个就像是在树上进行启发式合并。
简单的来说,把重儿子子树信息扔到父亲上,轻儿子则扔掉,这样可以保证复杂度\({n\log{n}}\)。(回头写博客吧)这个题还要维护链的信息所以dfs要多记一下信息。

代码

这个是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;
    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 了

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#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 MAXN = 1e5 + 5;
const int MAXC = 4e3 + 5;
int n, m, c;
struct Edge
{
    int ver, nxt;
} edge[MAXN << 1];
int head[MAXN], tot;
inline void add(const int &x, const int &y)
{
    edge[++tot].ver = y;
    edge[tot].nxt = head[x];
    head[x] = tot;
}
struct Point
{
    vector<int> vct;
    set<int> map;
    int x;
    // 子树大小  重儿子 路径儿子
    int siz, son, rson, deep;
    //节点颜色  子树颜色数    权值
    int color, sumcolor, sum;
} pt[MAXN];
int _cnt;
int dfn[MAXN];
int cnt[MAXC];
int rt[MAXN];
int f[MAXN][19];
inline bool cmp(const int &x, const int &y)
{
    return pt[x].rson < pt[y].rson;
}
inline bool cmp2(const Point &x, const Point &y)
{
    return dfn[x.x] < dfn[y.x];
}
//一遍bfs预处理求lca
inline void bfs()
{
    queue<int> q;
    int t = log(n) / log(2) + 1;
    q.push(1);
    pt[1].deep = 1;
    f[1][0] = 0;
    while (q.size())
    {
        register int x = q.front();
        q.pop();
        for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
        {
            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();
}
inline int lca(const int &X, const int &Y)
{
    int x = X, y = Y;
    int t = (log(n) / log(2)) + 1;
    if (pt[x].deep > pt[y].deep)
        swap(x, y);
    for (register int i = t; i >= 0; --i)
    {
        if (pt[f[y][i]].deep >= pt[x].deep)
            y = f[y][i];
    }
    if (x == y)
        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];
}
//处理重儿子,路径儿子,子树大小
void dfs1(int x)
{
    pt[x].siz = 1;
    pt[x].rson = x;
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
    {
        if (y == f[x][0])
            continue;
        dfs1(y);
        pt[x].siz += pt[y].siz;
        pt[x].rson = min(pt[x].rson, pt[y].rson);
        if (pt[pt[x].son].siz <= pt[y].siz)
            pt[x].son = y;
    }
}
void dfs2(int x)
{
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
        if (y != f[x][0])
            pt[x].vct.push_back(y);
    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();
}
void dfs3(int x)
{
    if (!cnt[pt[x].color])
        ++_cnt;
    cnt[pt[x].color]++;
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
    {
        if (y == f[x][0])
            continue;
        dfs3(y);
        pt[x].sumcolor += pt[y].sumcolor;
    }
    if (pt[x].son)
        rt[x] = rt[pt[x].son], pt[x].sumcolor = pt[pt[x].son].sumcolor;
    else
        rt[x] = x, pt[x].sumcolor = _cnt;
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
    {
        if (y == f[x][0] || y == pt[x].son)
            continue;
        y = rt[y];
        for (register set<int>::iterator it = pt[y].map.begin(); it != pt[y].map.end(); ++it)
        {
            register int tmp = *it;
            if (pt[rt[x]].map.find(tmp) == pt[rt[x]].map.end())
            {
                if (!cnt[tmp])
                    ++pt[x].sumcolor;
                pt[rt[x]].map.insert(tmp);
            }
        }
        pt[y].map.clear();
    }
    --cnt[pt[x].color];
    if (!cnt[pt[x].color])
        _cnt--;
    pt[rt[x]].map.insert(pt[x].color);
}
long long ans = 0, Ans = 0;
void dfs4(int x)
{
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
        if (y != f[x][0])
            pt[x].vct.push_back(y);
    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].sumcolor) - pt[x].deep + 1) + (1ll * pt[x].sum);
    Ans = max(ans, Ans);
    pt[x].vct.clear();
}
void dfs5(int x)
{
    for (register int i = head[x], y = edge[i].ver; i; i = edge[i].nxt, y = edge[i].ver)
    {
        if (y == f[x][0])
            continue;
        dfs5(y);
        pt[x].sum += pt[y].sum;
    }
}
int main()
{
#ifdef lky233
    freopen("testdata.in", "r", stdin);
    freopen("testdata.out", "w", stdout);
#endif
    n = poread<int>(), m = poread<int>(), c = poread<int>();
    for (register int i = 1; i <= n; ++i)
        pt[i].color = poread<int>();
    for (register int i = 1; i <= n; ++i)
        pt[i].x = i;
    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);
    dfs2(1);
    _cnt = 0;
    dfs3(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].sum += z;
        if (y)
            pt[y].sum += z;
        int L = lca(x, y);
        if (L)
            pt[L].sum -= z;
        if (f[L][0])
            pt[f[L][0]].sum -= z;
    }
    dfs5(1);
    dfs4(1);
    cerr << "ans: " << Ans << endl;
    cerr << clock() << "ms" << endl;
    cout << Ans << endl;
}

坑点

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

大概这样 画的难看

感谢nofind大佬解释题面,教我树上差分。
感谢Chrisk大佬指出排版错误,已修正。
ありがとうございました。

已完结

可能指不定哪天回来发个图解释一下做法

转载于:https://www.cnblogs.com/Shiina-Rikka/p/11541759.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值