其他分享
首页 > 其他分享> > P6157 有趣的游戏

P6157 有趣的游戏

作者:互联网

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;
}

标签:Node,sz,游戏,P6157,int,void,son,fi,有趣
来源: https://www.cnblogs.com/aitejiu/p/16211516.html