1. 程式人生 > 實用技巧 >「SCOI2016」美味

「SCOI2016」美味

題意

題目連結

做法

一個很新鮮的idea,由於直接建\(01\)trie沒法支援修改,不妨考慮另類做法:
列舉答案\(xor\) \(b\)(即列舉\(x_{i}+a_{j}\))的二進位制每一位是\(0\)還是\(1\),即對於最高位第\(t\)位,如果我想要這一位是\(0\)(因為\(b\)的這一位是\(1\)),那麼就判斷\([l,r]\)區間是否有\([0,2^{t}-1]\)的數字,後面的位類似。

至於判斷區間是否存在某個區間的數字,直接用動態開點線段樹搞就行了。

時間複雜度:\(O(nlog^2值域)\)

#include<cstdio>
#include<cstring>
#define  N  210000
#define  NN  4100000
using  namespace  std;
struct  node
{
	int  l,r,c/*數字個數*/;
}tr[NN];int  len,rt[N];
inline  void  updata(int  x){tr[x].c+=tr[tr[x].l].c+tr[tr[x].r].c;}
void  link(int  &x,int  l,int  r,int  k)
{
	if(!x)x=++len;
	tr[x].c++;
	if(l==r)return  ;
	int  mid=(l+r)>>1;
	if(k<=mid)link(tr[x].l,l,mid,k);
	else  link(tr[x].r,mid+1,r,k);
}
void  mer(int  &x,int  y)
{
	if(!x  ||  !y){x=x+y;return  ;}
	tr[x].c+=tr[y].c;
	mer(tr[x].l,tr[y].l);mer(tr[x].r,tr[y].r);
}
bool  query(int  x,int  y,int  l,int  r,int  ll,int  rr)
{
	if(tr[x].c==tr[y].c)return  0;
	else  if(l==ll  &&  r==rr)return  tr[x].c-tr[y].c>0;
	int  mid=(l+r)>>1;
	if(rr<=mid)return  query(tr[x].l,tr[y].l,l,mid,ll,rr);
	else  if(mid<ll)return  query(tr[x].r,tr[y].r,mid+1,r,ll,rr);
	else
	{
		if(!query(tr[x].l,tr[y].l,l,mid,ll,mid))return  query(tr[x].r,tr[y].r,mid+1,r,mid+1,rr);
		return  1;
	}
}
int  n,m,limit=262143;
int  main()
{
	scanf("%d%d",&n,&m);
	for(int  i=1;i<=n;i++)
	{
		int  x;scanf("%d",&x);
		link(rt[i],0,limit,x);
	}
	for(int  i=2;i<=n;i++)mer(rt[i],rt[i-1]);
	for(int  i=1;i<=m;i++)
	{
		int  b,x,l,r;scanf("%d%d%d%d",&b,&x,&l,&r);
		int  ans=0;
		for(int  j=17;j>=0;j--)
		{
			int  ll=ans,rr=ans;
			if(b&(1<<j))rr+=(1<<j)-1;//有
			else  ll+=(1<<j),rr+=(1<<(j+1))-1;//沒有 
			ll-=x;rr-=x;
			int  shit;
			if(ll<0  &&  rr<0)shit=0;//完全不行
			else
			{
				if(ll<0)ll=0;
				shit=query(rt[r],rt[l-1],0,limit,ll,rr);
			}
			ans|=(b&(1<<j))^(shit<<j); 
		}
		printf("%d\n",ans^b);
	}
	return  0;
}