列表

详情


NC19958. [FJOI2015]火星商店问题

描述

火星上的一条商业街里按照商店的编号1,2 ,…,n ,依次排列着n个商店。商店里出售的琳琅满目的商品中,每种商品都用一个非负整数val来标价。每个商店每天都有可能进一些新商品,其标价可能与已有商品相同。 
火星人在这条商业街购物时,通常会逛这条商业街某一段路上的所有商店,譬如说商店编号在区间[L,R]中的商店,从中挑选1件自己最喜欢的商品。每个火星人对商品的喜好标准各不相同。通常每个火星人都有一个自己的喜好密码x。对每种标价为val的商品,喜好密码为x的火星人对这种商品的喜好程度与val异或x的值成正比。也就是说,val xor x的值越大,他就越喜欢该商品。每个火星人的购物卡在所有商店中只能购买最近d天内(含当天)进货的商品。另外,每个商店都有一种特殊商品不受进货日期限制,每位火星人在任何时刻都可以选择该特殊商品。每个商店中每种商品都能保证供应,不存在商品缺货的问题。 
对于给定的按时间顺序排列的事件,计算每个购物的火星人的在本次购物活动中最喜欢的商品,即输出val xor x的最大值。这里所说的按时间顺序排列的事件是指以下2种事件:  
事件0,用三个整数0,s,v,表示编号为s的商店在当日新进一种标价为v 的商品。 
事件1,用5个整数1,L,R,x,d,表示一位火星人当日在编号为L到R的商店购买d天内的商品,该火星人的喜好密码为x。

输入描述

第1行中给出2个正整数n,m,分别表示商店总数和事件总数。
第2行中有n个整数,第i个整数表示商店i的特殊商品标价。
接下来的m行,每行表示1个事件。每天的事件按照先事件0,后事件1的顺序排列。

输出描述

将计算出的每个事件1的val xor x的最大值依次输出。

示例1

输入:

4 6
1 2 3 4
1 1 4 1 0
0 1 4
0 1 3
1 1 1 1 0
1 1 1 1 1
1 1 2 1 2

输出:

5
0
2
5

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(clang++11) 解法, 执行用时: 389ms, 内存消耗: 41140K, 提交时间: 2020-10-29 08:19:26

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG 
#define MAX 100100
#define lson (now<<1)
#define rson (now<<1|1)
#define pb(x) push_back(x)
inline int read()
{
    RG int x=0,t=1;RG char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Buy{int s,v,t;}q[MAX],tmp1[MAX],tmp2[MAX];
struct Ask{int l,r,tl,tr,x;}p[MAX];
bool cmp(Buy a,Buy b){return a.s<b.s;}
int rt[MAX];
namespace Trie//可持久化Trie
{
	struct Trie{int son[2],w;}t[MAX<<5];
	int tot,rt[MAX];
	void insert(int &x,int ff,int w,int now)
	{
		t[x=++tot]=t[ff];t[x].w++;
		if(now==-1)return;
		bool c=w&(1<<now);
		insert(t[x].son[c],t[ff].son[c],w,now-1);
	}
	int Query(int l,int r,int w,int now)
	{
		if(now==-1)return 0;
		bool c=w&(1<<now);
		int tmp=t[t[r].son[c^1]].w-t[t[l].son[c^1]].w;
		if(tmp)return Query(t[l].son[c^1],t[r].son[c^1],w,now-1)+(1<<now);
		else return Query(t[l].son[c],t[r].son[c],w,now-1);
	}
}
int n,m,ans[MAX];
vector<int> seg[MAX<<2];
int cnt1,cnt2;
//对于线段树的每个节点插入对应的询问
//线段树的每个节点代表着一个购买的时间
//然后对于每个线段树上的节点,维护哪些询问出现在了这些时间之中
//所以对于一个节点维护一个vector,将出现在这段时间中的询问放入vector中
void Modify(int now,int l,int r,int L,int R,int x)
{
	if(L>R)return;
	if(L<=l&&r<=R){seg[now].pb(x);return;}
	int mid=(l+r)>>1;
	if(L<=mid)Modify(lson,l,mid,L,R,x);
	if(R>mid)Modify(rson,mid+1,r,L,R,x);
}
int S[MAX],top;
//对于当前节点计算在区间内的答案
//考虑如何计算贡献,因为保证了当前节点内的所有询问的时间
//所以只需要考虑区间的问题了,因此按照区间维护可持久化Trie即可
int Binary(int x)
{
	int l=1,r=top,ret=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(S[mid]<=x)ret=mid,l=mid+1;
		else r=mid-1;
	}
	return ret;
}
void Calc(int now,int L,int R)
{
	top=Trie::tot=0;
	for(int i=L;i<=R;++i)
	{
		S[++top]=q[i].s;
		Trie::insert(rt[top],rt[top-1],q[i].v,17);
	}
	for(int i=0,len=seg[now].size();i<len;++i)
	{
		int k=seg[now][i];
		int l=Binary(p[k].l-1),r=Binary(p[k].r);
		ans[k]=max(ans[k],Trie::Query(rt[l],rt[r],p[k].x,17));
	}
}
void Divide(int now,int l,int r,int L,int R)
{
	if(L>R)return;int mid=(l+r)>>1,t1=0,t2=0;
	Calc(now,L,R);if(l==r)return;
	for(int i=L;i<=R;++i)
		if(q[i].t<=mid)tmp1[++t1]=q[i];
		else tmp2[++t2]=q[i];
	for(int i=1;i<=t1;++i)q[i+L-1]=tmp1[i];
	for(int i=1;i<=t2;++i)q[i+L-1+t1]=tmp2[i];
	Divide(lson,l,mid,L,L+t1-1);
	Divide(rson,mid+1,r,L+t1,R);
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=n;++i)Trie::insert(rt[i],rt[i-1],read(),17);
	for(int i=1;i<=m;++i)
	{
		int opt=read();
		if(!opt)
		{
			int s=read(),v=read();++cnt1;
			q[cnt1]=(Buy){s,v,cnt1};
		}
		else
		{
			int l=read(),r=read(),x=read(),d=read();
			ans[++cnt2]=Trie::Query(rt[l-1],rt[r],x,17);
			p[cnt2]=(Ask){l,r,max(1,cnt1-d+1),cnt1,x};
		}
	}
	for(int i=1;i<=cnt2;++i)Modify(1,1,cnt1,p[i].tl,p[i].tr,i);
	sort(&q[1],&q[cnt1+1],cmp);//按照商店的编号依次插入所有物品
	Divide(1,1,cnt1,1,cnt1);
	for(int i=1;i<=cnt2;++i)printf("%d\n",ans[i]);
	return 0;
}

上一题