bzoj 4811: [Ynoi2017]由乃的OJ

Description

由乃正在做她的OJ。现在她在处理OJ上的用户排名问题。OJ上注册了n个用户,编号为1~",一开始他们按照编号
排名。由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天
天问她题。。。因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她
在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送
Deus:这个题怎么做呀?
yuno:这个不是NOI2014的水题吗。。。
Deus:那如果出到树上,多组链询问,带修改呢?
yuno:诶。。。???
Deus:这题叫做睡觉困难综合征哟~
虽然由乃OI很好,但是她基本上不会DS,线段树都只会口胡,比如她NOI2016的分数就是100+100+100+0+100+100。
。。NOIP2017的分数是100+0+100+100+0+100所以她还是只能找你帮她做了。。。
给你一个有n个点的树,每个点的包括一个位运算opt和一个权值x,位运算有&,l,^三种,分别用1,2,3表示。
每次询问包含三个数x,y,z,初始选定一个数v。然后v依次经过从x到y的所有节点,每经过一个点i,v就变成v opti
 xi,所以他想问你,最后到y时,希望得到的值尽可能大,求最大值?给定的初始值v必须是在[0,z]之间。每次修
改包含三个数x,y,z,意思是把x点的操作修改为y,数值改为z

Input

第一行三个数n,m,k。k的意义是每个点上的数,以及询问中的数值z都 <2^k。之后n行
每行两个数x,y表示该点的位运算编号以及数值
之后n - 1行,每行两个数x,y表示x和y之间有边相连
之后m行,每行四个数,Q,x,y,z表示这次操作为Q(1位询问,2为修改),x,y,z意义如题所述
0 <= n , m <= 100000 , k <= 64

Output

对于每个操作1,输出到最后可以造成的最大刺激度v

      又是一道巧妙的题,被艾教拉上去没嘴出来的题。

      最简单的我们会想到维护每一个区间每一位0进去与1进去出来的答案,最简单就是开64颗线段树来进行维护,可是这样内存占用太大,接着我们就会发现每一位直接其实是没有关系的,所以将64颗线段树合并成一个unsigned long long进行维护。

       那么如何将两个区间合并呢,设左区间进1的答案为x1,进0的答案为x0,右区间进1的答案为y1,进0的答案为y0。

        我们先考虑进左边区间是进入0,右边出1的情况。第一种情况是左边出1,此时需要右边继续出1,那么就是x0与y1共有1的部分,x0&y1了,第二种情况是左边出0,右边还需要出1,那么就是x0取反之后与y0的答案即为(~x0)&(y0),最后左边区间进0的答案就是两者一或了。

        我们再考虑进左边区间是进入1,右边出1的情况。第一种情况是左边出1,此时需要右边继续出1,那么就是x1与y1共有1的部分,x1&y1了,第二种情况是左边出0,右边需要出1,那么就是x1取反之后与y0的答案即为(~x1)&(y0),最后左边区间进1的答案就是两者一或了。

         在实现的时候我们又会发现从左进与从右进答案是不一样的,所以需要开两颗线段树,一颗合并从左到右,另一颗从右向左合并即可。

        最后求解答案的时候,如果这一位答案能得到1,且加上它不超过要求大小,就贪心的选它作为答案的这一位,注意啦,要记得多加一个变量来存当前选择了的数字,因为有可能我选0出来的是1,答案和当前构成的数字并不一样哦。

        下附AC代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#define lson (now<<1)
#define rson ((now<<1)|1)
#define mid ((nl+nr)>>1)
#define maxn 100005
using namespace std;
typedef unsigned long long ull;
inline ull read()
{
    ull x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
ull n,m,k;
struct nod
{
    ull x1,x0;
    nod(ull op,ull now)
    {
        if(op==1)
        {
            x0=(0ull&now);
            x1=((~0ull)&now);
        }
        if(op==2)
        {
            x0=(0ull|now);
            x1=((~0ull)|now);
        }
        if(op==3)
        {
            x0=(0ull^now);
            x1=((~0ull)^now);
        }
    }
    nod(){x1=~0ull;x0=0;}
};
nod operator + (nod a,nod b)
{
    nod ans;
    ans.x0=(a.x0&b.x1)|((~a.x0)&b.x0);
    ans.x1=(a.x1&b.x1)|((~a.x1)&b.x0);
    return ans;
}
nod datl[maxn<<2],datr[maxn<<2];
void pushup(ull now)
{
    datl[now]=datl[lson]+datl[rson];
    datr[now]=datr[rson]+datr[lson];
}
void update(ull ql,ull qr,ull op,ull add,ull nl,ull nr,ull now)
{
    if(nl>nr)
    return;
    if(ql<=nl && nr<=qr)
    {
        datl[now]=datr[now]=nod(op,add);
        return;
    }
    if(ql<=mid)
    update(ql,qr,op,add,nl,mid,lson);
    if(mid<qr)
    update(ql,qr,op,add,mid+1,nr,rson);
    pushup(now);
}
nod queryl(ull ql,ull qr,ull nl,ull nr,ull now)
{
    if(nl>nr)
    {
        nod ans;
        ans.x0=ans.x1=0ull;
        return ans;
    }
    if(ql<=nl && nr<=qr)
    {
        return datl[now];
    }
    nod ans1,ans2;
    ull flag1=0,flag2=0;
    if(ql<=mid)
    {
        ans1=queryl(ql,qr,nl,mid,lson);
        flag1=1;
    }
    if(mid<qr)
    {
        ans2=queryl(ql,qr,mid+1,nr,rson);
        flag2=1;
    }
    if(flag1==1 && flag2==0) return ans1;
    else if(flag1==0 && flag1==1) return ans2;
    return ans1+ans2;
}
nod queryr(ull ql,ull qr,ull nl,ull nr,ull now)
{
    if(nl>nr)
    {
        nod ans;
        ans.x0=ans.x1=0ull;
        return ans;
    }
    if(ql<=nl && nr<=qr)
    {
        return datr[now];
    }
    nod ans1,ans2;
    ull flag1=0,flag2=0;
    if(ql<=mid)
    {
        ans1=queryr(ql,qr,nl,mid,lson);
        flag1=1;
    }
    if(mid<qr)
    {
        ans2=queryr(ql,qr,mid+1,nr,rson);
        flag2=1;
    }
    if(flag1==1 && flag2==0) return ans1;
    else if(flag1==0 && flag1==1) return ans2;
    return ans2+ans1;
}
ull cnt;
ull o[maxn],a[maxn];
ull dfn[maxn];
ull fa[maxn],anc[maxn],siz[maxn],dep[maxn];
vector<ull>edge[maxn];
void dfs1(ull now,ull pa)
{
    ull len=edge[now].size();
    siz[now]=1;
    fa[now]=pa;
    for(ull i=0;i<len;i++)
    {
        ull nex=edge[now][i];
        if(nex!=pa)
        {
            dep[nex]=dep[now]+1;
            dfs1(nex,now);
            siz[now]+=siz[nex];
        }
    }
}
void dfs2(ull now,ull top)
{
    dfn[now]=++cnt;
    anc[now]=top;
    ull len=edge[now].size();
    ull son=0;
    for(ull i=0;i<len;i++)
    {
        ull nex=edge[now][i];
        if(siz[nex]>siz[son] && nex!=fa[now])
        {
            son=nex;
        }
    }
    if(son!=0)
    dfs2(son,top);
    for(ull i=0;i<len;i++)
    {
        ull nex=edge[now][i];
        if(nex!=son && nex!=fa[now])
        {
            dfs2(nex,nex);
        }
    }
}
nod solve(ull p,ull q)
{
    nod ans1,ans2;
    while(anc[p]!=anc[q])
    {
        if(dep[anc[p]]>=dep[anc[q]])
        {
            ans1=ans1+queryr(dfn[anc[p]],dfn[p],1,n,1);
            p=fa[anc[p]];
        }
        else
        {
            ans2=queryl(dfn[anc[q]],dfn[q],1,n,1)+ans2;
            q=fa[anc[q]];
        }
    }
    if(dep[p]>dep[q])
    return ans1+queryr(dfn[q],dfn[p],1,n,1)+ans2;
    else 
    return ans1+queryl(dfn[p],dfn[q],1,n,1)+ans2;
}
int main()
{
    n=read();
    m=read();
    k=read();
    for(ull i=1;i<=n;i++)
    {
    	o[i]=read();
    	a[i]=read();
	}
    for(ull i=1;i<n;i++)
    {
        ull x,y;
        x=read();
        y=read();
        edge[x].push_back(y);
        edge[y].push_back(x);
    }
    dfs1(1ull,0ull);
    dfs2(1ull,1ull);
    for(ull i=1ull;i<=n;i++)
    {
        update(dfn[i],dfn[i],o[i],a[i],1,n,1);
    }
    while(m--)
    {
        ull f=read(),x=read(),y=read();
        ull z=read();
        if(f==2ull)
        {
            update(dfn[x],dfn[x],y,z,1ull,n,1ull);
        }
        else
        {
            nod ans=solve(x,y);
            ull res=0;
            ull now=0;
            for(ull i=k-1ull;i>=0;i--)
            {
                if(((ans.x0>>i)&1ull)==1ull)
                {
                    res+=(1ull<<i);
                }
                else if((now+(1ull<<i))<=z && ((ans.x1>>i)&1ull)==1ull)
                {
                    res+=(1ull<<i);
                    now+=(1ull<<i);
                }
                if(i==0)
                break;
            }
            printf("%llu\n",res);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值