zoukankan      html  css  js  c++  java
  • 【BZOJ4137】火星商店问题(线段树分治,可持久化Trie)

    【BZOJ4137】火星商店问题(线段树分治,可持久化Trie)

    题面

    洛谷
    BZOJ权限题

    题解

    显然可以树套树,外层线段树,内层可持久化Trie来做。
    所以我们需要更加优美的做法。——线段树分治。
    什么叫做线段树分治呢?
    我们发现每次询问都是区间的形式,看到区间我们就可以想到线段数。
    我们接着观察,发现了两个特点:询问是区间,加入新数是单点。
    那么我们对于单点构建线段树,在本题中这个单点是按照时间顺序构建的。
    所以每次询问在不考虑区间的情况下对应的时间是唯一的。
    所以把每次询问暴力放到线段树的每个节点上,用(vector)记录。
    那么,我们(dfs)遍历整棵线段树,
    我们发现每个询问已经不用再考虑时间的问题,而只需要考虑区间的问题。
    那么对于当前线段树时间区间的所有插入操作按照区间排序。
    构建可持久化(Trie)直接查答案就好了。
    代码应该注释海星。

    #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 register
    #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
    ",ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    PowerCat DNS 隧道通信
    各种反弹shell方法总结备忘
    Halo-个人独立博客系统
    内网渗透之域渗透
    使用 EW 作Socks5代理内网穿透
    PowerShell攻击:nishang
    贝叶斯网络
    Anaconda的CondaHTTPError问题
    完美解决win10系统无法安装.NET Framework问题
    敏捷开发中如何做质量管理?
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9379929.html
Copyright © 2011-2022 走看看