2018.10.25【NOIP练习】ZUA球困难综合征/记忆(线段树)(数位DP)(位运算骚操作)(按位贪心)...

传送门

(由于现在OJ上有两道重名的题,所以请先确认你要找的题解是不是这道题的)


解析:

这道题有两种做法,二元组维护信息+二进制数位DP,三元组维护信息+按位贪心。(其实二元组也可以按位贪心,三元组也可以数位DP。。。)

好写的二元组需要卡常才能过,因为数位DP还是太慢了。。。

解法1:二元组+数位DP

我们在线段树节点维护一个二元组(v0,v1)(v_0,v_1)(v0,v1)表示每一位上如果初始是0/10/10/1对应的结果。

这个如果更新如果做不好的话就是30位的枚举大常数,卡常数都不要想过这道题。

所以我们要找到一个O(1)O(1)O(1)小常数更新的方法,考虑合并两个儿子节点。
那么一个位上的数显然就是它在左儿子中的结果进入右儿子得到的结果,这个就很好做了(Latex打不了位运算符,大家将就着逻辑运算符看一下吧):v0=(lc.v0∧rc.v1)∨((∼lc.v0)∧rc.v0)v_0=(lc.v_0\wedge rc.v_1)\vee((\sim lc.v_0)\wedge rc.v_0)v0=(lc.v0rc.v1)((lc.v0)rc.v0)v1=(lc.v1∧rc.v1)∨((∼lc.v1)∧rc.v0)v_1=(lc.v_1\wedge rc.v_1)\vee ((\sim lc.v_1)\wedge rc.v_0)v1=(lc.v1rc.v1)((lc.v1)rc.v0)

其实就是直接将每个数进入左儿子的对应结果求出来映射到右儿子上。

之后区间询问出一个二元组(v0,v1)(v_0,v_1)(v0,v1)然后就可以数位DP了,注意由于这是最优解DP而不是计数类DP,请直接使用上下界剪枝。

解法2:三元组+按位贪心

三元组的位运算骚操作比二元组难多了啊,%一下DZYO,就是我就是看这位大佬的代码学会的三元组做这道题。

三元组的记录就有一些不同了,我们记录的不是每一个位对应的结果,而是这些结果被影响的方式。
这么说可能有点玄乎,直接进入正题。

我们记录(s,x,o)(s,x,o)(s,x,o)分别表示决定这些位的结果的是位与,异或还是位或。

大概是这么个意思:
sss中的“111”位:若当前位为111,则这一位结果为111,否则这一位结果为000sss中的“000”表示这一位的结果不被位与决定。
xxx中的“111”位:若当前位为111,则这一位的结果为000,否则这一位结果为111xxx中的“000”表示这一位的结果不被异或决定。
ooo中的对应位:位或的结果直接决定当前位的结果。

具体的就去我三元组的代码里面的getgetget看一下就懂了。

然后就是按位贪心,显然在l,rl,rl,r相同的前缀我们只能走唯一的选择,一旦出现不同,意味着如果我们走lll,之后的所有位都可以向上任取,反之如果我们走rrr,之后的所有位都可以向下任取,这个直接贪心一下就好了,复杂度远远小于数位DP。

就是三元组的位运算骚操作实在太多了,建议自己拿张纸和笔,手推一下 。(我也就推了一个上午而已,没办法太菜了)


代码(二元组):

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define re register 
#define gc get_char
#define pc putchar
#define cs const

cs int RLEN=1<<18|1;
inline char get_char() {
	static char ibuf[RLEN],*ib,*ob;
	(ib==ob) && (ob=(ib=ibuf)+fread(ibuf,1,RLEN,stdin));
	return (ib==ob) ? -1 : *ib++;
}

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline void outint(int a){
	static char ch[13];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=500005,lim=0x7fffffff;

struct node{
	int v0,v1;
	inline void assign(cs int &op,cs int &v){
		switch(op){
			case 0:{v0=0&v;v1=lim&v;break;}
			case 1:{v0=0|v;v1=lim|v;break;}
			case 2:{v0=0^v;v1=lim^v;break;}
		}
	}
	friend inline node merge(cs node &a,cs node &b){
		node t;
		t.v0=(a.v0&b.v1)|((~a.v0)&b.v0);
		t.v1=(a.v1&b.v1)|((~a.v1)&b.v0);
		return t;
	}
}t[N<<2],Ans;

inline void build(int k,int l,int r){
	if(l==r){
		int op=getint(),val=getint();
		t[k].assign(op,val);
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	t[k]=merge(t[k<<1],t[k<<1|1]);
}

inline void update(int k,int l,int r,cs int &pos,cs int &op,cs int &v){
	if(l==r){
		t[k].assign(op,v);
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)update(k<<1,l,mid,pos,op,v);
	else update(k<<1|1,mid+1,r,pos,op,v);
	t[k]=merge(t[k<<1],t[k<<1|1]);
}

inline void query(int k,int l,int r,cs int &ql,cs int &qr){
	if(ql<=l&&r<=qr)return (void)(Ans=merge(Ans,t[k]));
	int mid=(l+r)>>1;
	if(ql<=mid)query(k<<1,l,mid,ql,qr);
	if(qr>mid)query(k<<1|1,mid+1,r,ql,qr);
}

int lt[35],rt[35];
int g[2];
int f[35][2][2];
inline void dp(int v,int limitl,int limitr) {
	if(~f[v][limitl][limitr])return ;
	int a=limitl?lt[v]:0,b=limitr?rt[v]:1;
	if(v==0){
		for(int re i=a;i<=b;i++)
		f[v][limitl][limitr]=max(f[v][limitl][limitr],g[i]&(1<<v));
		return ;
	}
	for(int i=a;i<=b;i++) {
		dp(v-1,limitl&&i==lt[v],limitr&&i==rt[v]);
		f[v][limitl][limitr]=max(f[v][limitl][limitr],f[v-1][limitl&&i==lt[v]][limitr&&i==rt[v]]+(g[i]&(1<<v)));
	}
}

int query(int a,int b) {
	for(int i=0;i<31;++i,a>>=1) lt[i]=a&1;
	for(int i=0;i<31;++i,b>>=1) rt[i]=b&1;
	g[0]=Ans.v0,g[1]=Ans.v1;
	memset(f,-1,sizeof(f));
	dp(30,1,1);
	return f[30][1][1];
}

int n,m;
signed main(){
	n=getint();
	m=getint();
	build(1,1,n);
	while(m--){
		int op=getint();
		switch(op){
			case 1:{
				int pos=getint(),opt=getint(),v=getint();
				update(1,1,n,pos,opt,v);
				break;
			}
			case 2:{
				Ans.v0=0,Ans.v1=lim;
				int l=getint(),r=getint();
				query(1,1,n,l,r);
				l=getint(),r=getint();
				outint(query(l,r));pc('\n');
				break;
			}
		}
	}
	return 0;
}

代码(三元组):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

inline void outint(int a){
	static char ch[13];
	if(a==0)pc('0');
	while(a)ch[++ch[0]]=a-a/10*10,a/=10;
	while(ch[0])pc(ch[ch[0]--]^48);
}

cs int N=500005,lim=0x7fffffff;
struct node{
	int s,x,o;//and,xor,or
	node(){}
	inline friend node merge(cs node &a,cs node &b){
		node t;
		t.s=(a.s&b.s)|(a.x&b.x);
		t.x=(a.s&b.x)|(a.x&b.s);
		t.o=lim^a.s^a.x;
		t.o=((a.o&b.x)^(b.x&t.o))|(a.o&b.s);
		t.o|=b.o;
		return t;
	}
	inline void assign(cs int &op,cs int &v){
		switch(op){
			case 0:{s=v;x=0;o=0;break;}
			case 1:{s=lim^v;x=0;o=v;break;}
			case 2:{s=lim^v;x=v;o=0;break;}
		}
	}
}t[N<<2],Ans;

int n,m;

inline void build(int k,int l,int r){
	if(l==r){
		int op=getint(),val=getint();
		t[k].assign(op,val);
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	t[k]=merge(t[k<<1],t[k<<1|1]);
}

inline void update(int k,int l,int r,cs int &pos,cs int &op,cs int &v){
	if(l==r)return t[k].assign(op,v);
	int mid=(l+r)>>1;
	if(pos<=mid)update(k<<1,l,mid,pos,op,v);
	else update(k<<1|1,mid+1,r,pos,op,v);
	t[k]=merge(t[k<<1],t[k<<1|1]); 
}

inline node query(int k,int l,int r,cs int &ql,cs int &qr){
	if(ql<=l&&r<=qr)return t[k];
	int mid=(l+r)>>1;
	if(qr<=mid)return query(k<<1,l,mid,ql,qr);
	if(mid<ql)return query(k<<1|1,mid+1,r,ql,qr);
	return merge(query(k<<1,l,mid,ql,qr),query(k<<1|1,mid+1,r,ql,qr));
}

int l,r;

inline int get(int i,bool val){
	if(Ans.s&(1<<i))return val?1:0;
	if(Ans.x&(1<<i))return val?0:1;
	return (Ans.o>>i)&1;
}

inline int go_l(int i){
	if(i==-1)return 0;
	if(get(i,1)>=get(i,0)){
		int v=get(i,1)<<i;
		if(!((l>>i)&1)){
			int limit=(1<<i)-1;
			int o=limit^(Ans.s&limit)^(Ans.x&limit);
			return v|(((o&Ans.o)|Ans.s|Ans.x)&limit);
		}
		else return v|go_l(i-1);
	}else if(!((l>>i)&1))return (1<<i)|go_l(i-1);
	else return go_l(i-1);
}

inline int go_r(int i){
	if(i==-1)return 0;
	if(get(i,0)>=get(i,1)){
		int v=get(i,0)<<i;
		if((r>>i)&1){
			int limit=(1<<i)-1;
			int o=limit^(Ans.s&limit)^(Ans.x&limit);
			return v|(((o&Ans.o)|Ans.s|Ans.x)&limit);
		}
		else return v|go_r(i-1);
	}else if((r>>i)&1)return (1<<i)|go_r(i-1);
	else return go_r(i-1);
}

inline int query(){
	int res=0;
	for(int re i=30;~i;--i){
		if(((l>>i)&1)==((r>>i)&1)){
			res|=get(i,(l>>i)&1)<<i;
			continue;
		}
		if(get(i,(l>>i)&1)==get(i,(r>>i)&1))res|=(get(i,(l>>i)&1)<<i)|max(go_l(i-1),go_r(i-1));
		else if(get(i,(l>>i)&1)>get(i,(r>>i)&1))res|=go_l(i);
		else res|=go_r(i);
		break;
	}
	return res;
}

signed main(){
	n=getint();
	m=getint();
	build(1,1,n);
	while(m--){
		int op=getint();
		switch(op){
			case 1:{
				int pos=getint(),opt=getint(),val=getint();
				update(1,1,n,pos,opt,val);
				break;
			}
			case 2:{
				l=getint(),r=getint();
				Ans=query(1,1,n,l,r);
				l=getint(),r=getint();
				outint(query());pc('\n');
				break;
			}
		}
	}
	return 0;
} 

转载于:https://www.cnblogs.com/zxyoi/p/10047162.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值