HOJ1127听风
题面(题面难懂这就是语文题)
一棵根节点为1的树,所有边权均为1,每个节点有不同的颜色C。对这棵树进行游览并统计贡献。
游览方式
- 如果一个节点被游览过就不再游览。
- 如果在i号节点,它是叶子节点或者所有儿子都被游览过则停在该节点。
- 如果第 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
一个好题
这个就像是在树上进行启发式合并。
简单的来说,把重儿子子树信息扔到父亲上,轻儿子则扔掉,这样可以保证复杂度({nlog{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大佬指出排版错误,已修正。
ありがとうございました。
已完结
可能指不定哪天回来发个图解释一下做法