题目描述
游戏在一棵大小为 n 的树上进行。其中每个点都有点权,第 i 个点的点权为 wi。
每一次系统会给出一条链,小 A 可以从这条链上找出两个点权不同的点 x,y,他的得分是 wxmodwy。然后小 B 会从整棵树中选取两个小 A 没有选过的点,计分方式同小 A。
为了保持游戏难度,系统有时会增加一个点的权值。
当然,小 A 会尽可能使自己得分最大,他想知道这个值是多少。同时,他想知道,在自己得分最大的情况下,小 B 的最大得分是多少。
思路:
根据取模的性质可以知道, 如果 wx<wy ,那 wxmodwy=wx ,否则 (wxmodwy)<wy (wx≥wy) 。所以如果要让所选出来的数相互取模之后尽可能的大,就要让两数中较小的那一个数作为 wx 较大的数作为 wy 。所以只需要知道每一次链中的最大值和严格次大值,以及剩下的整棵树中的最大值和严格次大值就行了。那么链中的最大值和严格次大值,可以用 树链剖分后建出的线段树来维护。
struct node {
int v, nxt;
}e[N * 2];
int idx, h[N];
void add(int a, int b) {
e[++idx] = {b, h[a]}, h[a] = idx;
}
int n;
int dfn[N], dep[N], siz[N], son[N], f[N], top[N], cnt = 0;
int po[N], w[N];
void dfs1(int u = 1, int fa = 0) {
siz[u] = 1, f[u] = fa, dep[u] = dep[fa] + 1;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u = 1, int tp = 1) {
dfn[u] = ++cnt;
top[u] = tp;
po[cnt] = w[u];
if (!son[u]) return ;
dfs2(son[u], tp);
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == f[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct Info {
std::pair<int, int> maxv;
} tr[N << 2];
std::pair<int, int> mergemax(std::pair<int, int> a, std::pair<int, int> b) {
std::pair<int, int> c;
c.first = std::max(a.first, b.first);
if (a.first != b.first) {
c.second = std::min(a.first, b.first);
c.second = std::max(c.second, std::max(a.second, b.second));
return c;
}
c.second = std::max(a.second, b.second);
return c;
}
void build(int u = 1, int l = 1, int r = n) {
if (l == r) return void(tr[u].maxv = {po[l], -inf});
int mid = (l + r) >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
tr[u].maxv = mergemax(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}
void modify(int pos, int v, int u = 1, int l = 1, int r = n) {
if (l > pos || r < pos) return;
if (l == r) return void(tr[u].maxv = {tr[u].maxv.first + v, -inf});
int mid = (l + r) >> 1;
modify(pos, v, u << 1, l, mid);
modify(pos, v, u << 1 | 1, mid + 1, r);
tr[u].maxv = mergemax(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}
std::pair<int, int> query(int ln, int rn, int u = 1, int l = 1, int r = n) {
if (l > rn || r < ln) return std::make_pair(-inf, -inf);
if (l >= ln && r <= rn) return tr[u].maxv;
int mid = (l + r) >> 1;
return mergemax(query(ln, rn, u << 1, l, mid), query(ln, rn, u << 1 | 1, mid + 1, r));
}
std::pair<int, int> ask(int u, int v) {
std::pair<int, int> ans = {-inf, -inf};
while(top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) std::swap(u, v);
ans = mergemax(ans, query(dfn[top[u]], dfn[u]));
u = f[top[u]];
}
if (dep[u] > dep[v]) std::swap(u, v);
ans = mergemax(ans, query(dfn[u], dfn[v]));
return ans;
}
整棵树上的最大值和严格次大值可以考虑用平衡树来维护,这样也可以解决严格次大中涉及到的重复的问题。
主体部分注意一下单点修改的时候先从平衡树中删除该节点后再加回去,查询四个最大值的时候,先将链中的最大值和严格次大值求出来,从平衡树中删除出去后再查询区间第二大。
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!