P6157 有趣的游戏
分析
还是一样,看一看题目要求。
每一次系统会给出一条链,小 A 可以从这条链上找出两个点权不同的点 x,y,他的得分是 \(w_x mod w_y\)。然后小 B 会从整棵树中选取两个小 A 没有选过的点,计分方式同小 A。
非常容易推理出,对于A而言,其选出的最大答案是选出一条链的最大值与次大值,则对A而言最优解就是次大值
则对于B而言,B需要从被A扣出两个点的树中,选出最大值与次大值,对B来说最优的就是这个次大值
这个题最难的对我而言其实是,怎么从把A选的两个点抠出来了。
答案非常简单,用multiset就可以解决了。
直接从multiset中将A选中的删掉,再从multiset中找到最大值和次大值即可。
细节在代码中有注释。直接看。
Ac_code
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10,M = N * 2;
struct Node
{
int l,r,fi,se;
}tr[N<<2];
int h[N],e[M],ne[M],w[N],idx;
int sz[N],fa[N],son[N],dep[N];
int top[N],id[N],nw[N],ts;
multiset<int> s;
int n,q;
void add(int a,int b)
{
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void dfs1(int u,int pa,int depth)
{
sz[u] = 1,dep[u] = depth;
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==pa) continue;
fa[j] = u;
dfs1(j,u,depth+1);
sz[u] += sz[j];
if(sz[j]>sz[son[u]]) son[u] = j;
}
}
void dfs2(int u,int tp)
{
top[u] = tp,id[u] = ++ts,nw[ts] = w[u];
if(!son[u]) return ;
dfs2(son[u],tp);
for(int i=h[u];~i;i=ne[i])
{
int j = e[i];
if(j==fa[u]||j==son[u]) continue;
dfs2(j,j);
}
}
void push(Node &u,Node l,Node r)
{
u.fi = max(l.fi,r.fi);
u.se = max(l.fi==u.fi?l.se:l.fi,r.fi==u.fi?r.se:r.fi);
}
void pushup(int u)
{
push(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
if(l==r)
{
tr[u] = {l,l,nw[l],-1};//因为维护的是严格次小值,因此,当区间长为1时,记得初始化次小值为-1,wa死我了
return ;
}
tr[u] = {l,r};
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int x,int k)
{
if(tr[u].l==tr[u].r)
{
tr[u].fi += k;////因为维护的是严格次小值,因此,当区间长为1时,记得初始化次小值为-1,wa死我了
//因为这个原因,所有单点修的时候,就不要改次小值了,还是-1
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x<=mid) modify(u<<1,x,k);
else modify(u<<1|1,x,k);
pushup(u);
}
Node query(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
Node res = {0,0,-1,-1};
if(l>mid) return query(u<<1|1,l,r);
else if(r<=mid) return query(u<<1,l,r);
else
{
auto left = query(u<<1,l,r),right = query(u<<1|1,l,r);
push(res,left,right);
return res;
}
}
int main()
{
scanf("%d",&n);
memset(h,-1,sizeof h);
for(int i=0;i<n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
for(int i=1;i<=n;i++)
{
int x;scanf("%d",&x);
w[i] = x;s.insert(x);
}
scanf("%d",&q);
dfs1(1,-1,1);
dfs2(1,1);
build(1,1,n);
while(q--)
{
int op,x,y;scanf("%d%d%d",&op,&x,&y);
if(!op)
{
s.erase(s.lower_bound(w[x]));//嗷,还有,单点修改后,记得将s中的对应删掉
//同时,给向我一样语法不好的同学,提一句,multiset里不要之间删数值,会把所有对应值删掉
//因此,可以先lower_bound找到,再删找到的对应迭代器的位置。
w[x] += y;
s.insert(w[x]);//也不要忘记再加上
modify(1,id[x],y);
}
else
{
Node res = {0,0,-1,-1};
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
push(res,res,query(1,id[top[x]],id[x]));
x = fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
push(res,res,query(1,id[y],id[x]));
if(res.se==-1) puts("-1");
else
{
s.erase(s.lower_bound(res.fi)),s.erase(s.lower_bound(res.se));
printf("%d %d\n",res.se,*(--s.lower_bound(*--s.end())));//这里也是,找到最大值后,其前边的值不一定是次大值,直接二分查找
s.insert(res.fi),s.insert(res.se);
}
}
}
return 0;
}