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听风的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp HOJ - 302:最大平均值[AC]; http://hoj.twbbs.org/judge/judge/submission/21872