zoukankan      html  css  js  c++  java
  • BZOJ4811: [Ynoi2017]由乃的OJ

    BZOJ4811: [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

    Sample Input

    5 5 3
    1 7
    2 6
    3 7
    3 6
    3 1
    1 2
    2 3
    3 4
    1 5
    1 1 4 7
    1 1 3 5
    2 1 1 3
    2 3 3 3
    1 1 3 2

    Sample Output

    7
    1
    5

    题解Here!

    起床困难综合症上树。。。

    建议先把那个签到题$A$了:

    BZOJ3668: [Noi2014]起床困难综合症

    好麻烦啊。。。

    首先一个不变的套路——树链剖分。

    我们发现,我们可以用于原题同样的办法来求解。

    也即是说,我们用$0$和$(1111111...1)_2$来从前到后取一遍,再从后向前取一遍。

    之后再一位一位地贪心。

    这样的话复杂度是$O(nlog_2^2n)$的。

    然后问题就变成了:如何维护从前到后与从后到前的取值?

    带修改,没有可差分性,于是使用线段树。

    线段树每个节点维护四个值:

    $0$从前到后取一遍的值,$(1111111...1)_2$从前到后取一遍的值;

    $0$从后向前取一遍的值,$(1111111...1)_2$从后向前取一遍的值。

    假设当前要合并的线段树节点是$l$和$r$,$f_0[l]$的第$i$位代表这一位是用$0$取一遍的值出来的值,$f_1[l]$同理。

    那么显然,对于新的合并后节点$u$而言,如果$f_0[u]$的第$i$位(后文中用$f_0[u][i]$表示)是$1$,那么有以下两种情况:

    情况一:$f_0[l][i]==1&&f_1[r][i]==1$

    情况二:$f_0[l][i]==0&&f_0[r][i]==1$

    可以得到表达式:
    $$f_0[u][i]=(f_0[l][i]&f_1[r][i])|(!f_0[l][i]&f_0[r][i])$$

    那么把这个式子放到整个$f_0[u]$的角度上,就是:
    $$f_0[u]=((f_0[l]&f_1[r])|((!f_0[l])&f_1[r]))$$

    其中$&,|,!$分别是按位与,或,取反。

    对于$f_1[u]$,也是同样的道理:
    $$f_1[u]=((f_1[l]&f_1[r])|((!f_1[l])&f_0[r]))$$

    反过来取一遍的同理。

    然后我们就可以维护这玩意了。。。

    但是代码量。。。

    还有,要开$unsigned long long$。。。

    还有,我们求出来两条路径:

    $u->LCA(u,v),LCA(u,v)->v$

    但是其中一个要把顺序反过来。

    然后就没了。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define LSON rt<<1
    #define RSON rt<<1|1
    #define DATAL(x) a[x].data0
    #define DATAR(x) a[x].data1
    #define LSIDE(x) a[x].l
    #define RSIDE(x) a[x].r
    #define MAXN 100010
    using namespace std;
    const unsigned long long MIN=0,MAX=~MIN;
    int n,m,q,c=1,d=1,top1,top2;
    int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],id[MAXN],pos[MAXN],top[MAXN];
    struct Tree{
    	int next,to;
    }tree[MAXN<<1];
    struct data{
    	unsigned long long v,w;
    	friend data operator +(const data &p,const data &q){
    		data now;
    		now.v=(p.v&q.w)|((~p.v)&q.v);
    		now.w=(p.w&q.w)|((~p.w)&q.v);
    		return now;
    	}
    }val[MAXN];
    struct Segment_Tree{
    	data data0,data1;
    	int l,r;
    	friend Segment_Tree operator +(const Segment_Tree &p,const Segment_Tree &q){
    		Segment_Tree now;
    		now.data0=p.data0+q.data0;now.data1=q.data1+p.data1;
    		now.l=p.l;now.r=q.r;
    		return now;
    	}
    }a[MAXN<<2],ans1[MAXN],ans2[MAXN];
    inline long long read(){
    	long long date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline data calculate(int operate,unsigned long long x){
    	switch(operate){
    		case 1:return (data){MIN&x,MAX&x};
    		case 2:return (data){MIN|x,MAX|x};
    		case 3:return (data){MIN^x,MAX^x};
    	}
    }
    inline void add_edge(int x,int y){
    	tree[c].to=y;tree[c].next=head[x];head[x]=c++;
    	tree[c].to=x;tree[c].next=head[y];head[y]=c++;
    }
    void dfs1(int rt){
    	son[rt]=0;size[rt]=1;
    	for(int i=head[rt],will;i;i=tree[i].next){
    		will=tree[i].to;
    		if(!deep[will]){
    			deep[will]=deep[rt]+1;
    			fa[will]=rt;
    			dfs1(will);
    			size[rt]+=size[will];
    			if(size[son[rt]]<size[will])son[rt]=will;
    		}
    	}
    }
    void dfs2(int rt,int f){
    	id[rt]=d++;pos[id[rt]]=rt;top[rt]=f;
    	if(son[rt])dfs2(son[rt],f);
    	for(int i=head[rt],will;i;i=tree[i].next){
    		will=tree[i].to;
    		if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
    	}
    }
    inline void pushup(int rt){
    	DATAL(rt)=DATAL(LSON)+DATAL(RSON);
    	DATAR(rt)=DATAR(RSON)+DATAR(LSON);
    }
    void buildtree(int l,int r,int rt){
    	LSIDE(rt)=l;RSIDE(rt)=r;
    	if(l==r){
    		DATAL(rt)=DATAR(rt)=val[pos[l]];
    		return;
    	}
    	int mid=l+r>>1;
    	buildtree(l,mid,LSON);
    	buildtree(mid+1,r,RSON);
    	pushup(rt);
    }
    void update(int k,data c,int rt){
    	if(LSIDE(rt)==RSIDE(rt)){
    		DATAL(rt)=DATAR(rt)=c;
    		return;
    	}
    	int mid=LSIDE(rt)+RSIDE(rt)>>1;
    	if(k<=mid)update(k,c,LSON);
    	else update(k,c,RSON);
    	pushup(rt);
    }
    Segment_Tree query(int l,int r,int rt){
    	if(l<=LSIDE(rt)&&RSIDE(rt)<=r)return a[rt];
    	int mid=LSIDE(rt)+RSIDE(rt)>>1;
    	if(r<=mid)return query(l,r,LSON);
    	else if(mid<l)return query(l,r,RSON);
    	else return (query(l,mid,LSON)+query(mid+1,r,RSON));
    }
    Segment_Tree query_path(int x,int y){
    	Segment_Tree ans;
    	top1=top2=0;
    	while(top[x]!=top[y]){
    		if(deep[top[x]]>=deep[top[y]]){
    			ans1[++top1]=query(id[top[x]],id[x],1);
    			x=fa[top[x]];
    		}
    		else{
    			ans2[++top2]=query(id[top[y]],id[y],1);
    			y=fa[top[y]];
    		}
    	}
    	if(deep[x]>deep[y])ans1[++top1]=query(id[y],id[x],1);
    	else ans2[++top2]=query(id[x],id[y],1);
    	for(int i=1;i<=top1;i++)swap(ans1[i].data0,ans1[i].data1);
    	if(top1){
    		ans=ans1[1];
    		for(int i=2;i<=top1;i++)ans=ans+ans1[i];
    		if(top2)ans=ans+ans2[top2];
    	}
    	else ans=ans2[top2];
    	for(int i=top2-1;i>=1;i--)ans=ans+ans2[i];
    	return ans;
    }
    void solve(int x,int y,unsigned long long z){
    	unsigned long long ans=0;
    	data t;
    	Segment_Tree s=query_path(x,y);
    	for(int i=63;i>=0;i--){
    		t.v=(s.data0.v>>i)&1uLL;
    		t.w=(s.data0.w>>i)&1uLL;
    		if(t.v>=t.w||(1uLL<<i)>z)ans|=(t.v?(1uLL<<i):0);
    		else{
    			ans|=(t.w?(1uLL<<i):0);
    			z-=(1ull<<i);
    		}
    	}
    	printf("%llu
    ",ans);
    }
    void work(){
    	int f;
    	unsigned long long x,y,z;
    	while(m--){
    		f=read();x=read();y=read();z=read();
    		if(f==1){
    			solve(x,y,z);
    		}
    		else{
    			update(id[x],calculate(y,z),1);
    		}
    	}
    }
    void init(){
    	int x,y;
    	unsigned long long k;
    	n=read();m=read();q=read();
    	for(int i=1;i<=n;i++){
    		x=read();k=read();
    		val[i]=calculate(x,k);
    	}
    	for(int i=1;i<n;i++){
    		x=read();y=read();
    		add_edge(x,y);
    	}
    	deep[1]=1;
    	dfs1(1);
    	dfs2(1,1);
    	buildtree(1,n,1);
    }
    int main(){
    	init();
    	work();
    	return 0;
    }
    /*
    
    Result: Accepted
    
    Time: 3516 ms
    
    Because of: Xuan_Xue......
    
    */
    

    $LCT$大法:

    这个题能用树剖做,那为什么不能$LCT$呢?

    每个节点维护四个值:

    两个为中序遍历这棵子树的$f_0,f_1$(分别表示$0$和$MAX$(二进制下全为$1$)跑的答案)

    另外两个为中序遍历的反向遍历这棵子树的$f_0,f_1$。

    还要记得保存这个点的初始操作。

    合并:对于中序遍历的若知道的左边的$f_0,f_1$,右边的$g_0,g_1$,合并后的$h_0,h_1$有:

    $$h_0=(!f_0&g_0)|(f_0&g_1)$$
    $$h_1=(!f_1&g_0)|(f_1&g_1)$$

    反向中序遍历同理。

    注意$LCT$丢到$Splay$后翻转操作的修改。

    剩下的与NOI2014那道签到题一样,贪心。。。

    复杂度为$O(nlog_2n)$。

    理论上是能过的。。。但是似乎被卡常了。。。(什么似乎,就是。。。)

    所以还是去洛谷开$O2$吧。。。

    洛谷P3613 睡觉困难综合征

    毒瘤lxl成功地把一道原题出道了$ ext{由乃}OI$。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 100010
    using namespace std;
    unsigned long long MIN=0,MAX=~MIN;
    int n,m,q,c=1;
    struct node1{
        unsigned long long v,w;
    }val[MAXN];
    struct node2{
        int f,flag,son[2];
        node1 l,r;
    }a[MAXN];
    inline long long read(){
    	long long date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    inline long long operation(long long x,long long y,int opt){
        if(!opt)return x;
        switch(opt){
            case 1:{x&=y;break;}
            case 2:{x|=y;break;}
            case 3:{x^=y;break;}
        }
        return x;
    }
    inline node1 update(node1 x,node1 y){
        node1 rt;
        rt.v=(~x.v&y.v)|(x.v&y.w);
        rt.w=(~x.w&y.v)|(x.w&y.w);
        return rt;
    }
    inline bool isroot(int rt){
        return a[a[rt].f].son[0]!=rt&&a[a[rt].f].son[1]!=rt;
    }
    inline void pushup(int rt){
        if(!rt)return;
        a[rt].l=a[rt].r=val[rt];
        if(a[rt].son[0]){a[rt].l=update(a[a[rt].son[0]].l,a[rt].l);a[rt].r=update(a[rt].r,a[a[rt].son[0]].r);}
        if(a[rt].son[1]){a[rt].l=update(a[rt].l,a[a[rt].son[1]].l);a[rt].r=update(a[a[rt].son[1]].r,a[rt].r);}
    }
    inline void reserve(int rt){
        swap(a[rt].l,a[rt].r);swap(a[rt].son[0],a[rt].son[1]);
        a[rt].flag^=1;
    }
    inline void pushdown(int rt){
        if(!rt||!a[rt].flag)return;
        reserve(a[rt].son[0]);reserve(a[rt].son[1]);
        a[rt].flag^=1;
    }
    inline void turn(int rt){
        int x=a[rt].f,y=a[x].f,k=a[x].son[0]==rt?1:0;
        if(!isroot(x)){
            if(a[y].son[0]==x)a[y].son[0]=rt;
            else a[y].son[1]=rt;
        }
        a[rt].f=y;a[x].f=rt;a[a[rt].son[k]].f=x;
        a[x].son[k^1]=a[rt].son[k];a[rt].son[k]=x;
        pushup(x);pushup(rt);
    }
    void splay(int rt){
        int top=0,stack[MAXN];
        stack[++top]=rt;
        for(int i=rt;!isroot(i);i=a[i].f)stack[++top]=a[i].f;
        while(top)pushdown(stack[top--]);
        while(!isroot(rt)){
            int x=a[rt].f,y=a[x].f;
            if(!isroot(x)){
                if((a[y].son[0]==x)^(a[x].son[0]==rt))turn(rt);
                else turn(x);
            }
            turn(rt);
        }
    }
    void access(int rt){
        for(int i=0;rt;i=rt,rt=a[rt].f){
            splay(rt);
            a[rt].son[1]=i;
            pushup(rt);
        }
    }
    inline void makeroot(int rt){access(rt);splay(rt);reserve(rt);}
    inline void split(int x,int y){makeroot(x);access(y);splay(y);}
    inline void link(int x,int y){makeroot(x);a[x].f=y;}
    inline void answer(unsigned long long sum0,unsigned long long sum1,unsigned long long maxn){
        unsigned long long ans=0,t=1;
        for(int i=63;i>=0;i--){
            if(sum0&(t<<i))ans+=(t<<i);
            else if(maxn>=(t<<i)&&(sum1&(t<<i))){
                maxn-=(t<<i);
                ans+=(t<<i);
            }
        }
        printf("%llu
    ",ans);
    }
    void work1(int x,int y,unsigned long long k){
        split(x,y);
        answer(a[y].l.v,a[y].l.w,k);
    }
    void work2(int x,int y,unsigned long long k){
        switch(y){
            case 1:val[x]=(node1){MIN,k};break;
            case 2:val[x]=(node1){k,MAX};break;
            case 3:val[x]=(node1){k,~k};break;
        }
        splay(x);
    }
    void work(){
        int f,x,y;
        unsigned long long k;
        while(m--){
            f=read();x=read();y=read();k=read();
            if(f==1)work1(x,y,k);
            if(f==2)work2(x,y,k);
        }
    }
    void init(){
        int x,y;
        unsigned long long k;
        n=read();m=read();q=read();
        for(int i=1;i<=n;i++){
            x=read();k=read();
            switch(x){
                case 1:val[i]=(node1){MIN,k};break;
                case 2:val[i]=(node1){k,MAX};break;
                case 3:val[i]=(node1){k,~k};break;
            }
        }
        for(int i=1;i<n;i++){
            x=read();y=read();
            link(x,y);
        }
    }
    int main(){
        init();
        work();
    	return 0;
    }
    /*
    
    Result: Time_Limit_Exceed
    
    Because of: chang_shu.....
    
    Solution: No_Solution......
    
    */
    
  • 相关阅读:
    LED点阵书写显示
    cpld fpga 区别
    SublimeText 自带格式化代码功能
    iText C# 合并PDF文件流,以及A5变A4时内容默认放在最底下的问题的解决方法;ASP.NET 实现Base64文件流下载PDF
    Adobe Acrobat 9 Pro序列号
    c#比较两个数组的差异
    iNotify.js通知JS 实现浏览器的 title 闪烁、滚动、声音提示、chrome、Firefox、Safari等系统通知。
    配置IISExpress允许外部访问
    https://sweetalert2.github.io/
    c# 利用MailKit.IMap 收取163邮件
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9940950.html
Copyright © 2011-2022 走看看