Interval(01 想法 主席树)

https://ac.nowcoder.com/acm/contest/5670/H

题意:

Interval(01 想法 主席树)_第1张图片
解析:

对于每一个右端点,F值不同的左端点至多为30个,所以我们只需要维护30n个 [ l , r , k = F ( l , r ) ] [l,r,k=F(l,r)] [l,r,k=F(l,r)]

建立主席树,当右端点为 r r r,枚举 [ l , r , k = F ( l , r ) ] [l,r,k=F(l,r)] [l,r,k=F(l,r)]

  • k k k未出现过,则直接在 l l l的位置+1,这样子查询 [ L , R ] [L,R] [L,R]时贡献便+1。
  • 如果出现过,我们对于右端点来说,需要左端点较为靠右的那个 k k k。进行比较,若当前左端点大于之前的K的左端点,则在原来的 l l l的位置-1,新的 l l l的位置+1。

最后 [ L , R ] [L,R] [L,R]的答案就是 R R R代表的主席树上,区间 [ L , R ] [L,R] [L,R]的和。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-07-25-10.08.08
 */
#include
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int N=1e5+9;
const int inf=0x3f3f3f3f;
LL read(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd read()
/*_________________________________________________________begin*/

int n;
int a[N];
int nxt[N][33];
int cnt;
struct node{
	int l,r,k;
	bool operator<(const node &A)const{
		return r<A.r;
	}
}e[N*30];
int cntX=0;
void work()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
	}
	for(int i=0;i<30;i++)nxt[0][i]=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<30;j++)
		{
			nxt[i][j]=((1<<j)&a[i])==0?i:nxt[i-1][j];
		}
	}
	map<int,int>mp;
	for(int i=1;i<=n;i++)
	{
		int now=a[i],pos=i-1;
		e[++cnt]={i,i,now};mp[now]=1;
		while(1)
		{
			int minv=0;
			for(int j=0;j<30;j++)
			{
				if(((1<<j)&now)==0)continue;
				minv=max(minv,nxt[pos][j]);
			}
			if(minv==0)
			{
				break;
			}
			now=now&a[minv];
			pos=minv;
			e[++cnt]={pos,i,now};mp[now]=1;
		}
	}

	for(auto &x:mp)x.second=++cntX;
	for(int i=1;i<=cnt;i++)
	{
		//printf("before %d %d %d\n",e[i].l,e[i].r,e[i].k);
		e[i].k=mp[e[i].k];
		//printf("after %d %d %d\n",e[i].l,e[i].r,e[i].k);
	}

}

int rt[N],ls[N*40*30],rs[N*40*30],cntTree,siz[N*40*30];

void insert(int last,int now,int l,int r,int p,int value){
    siz[now]=siz[last]+value;
    ls[now]=ls[last];
    rs[now]=rs[last];//直接连原来版本的儿子
    if(l==r) return ;
    int mid=l+r>>1;
    if(p<=mid)insert(ls[last],ls[now]=++cntTree,l,mid,p,value);//需要更新才建新点当儿子
    else insert(rs[last],rs[now]=++cntTree,mid+1,r,p,value);
}

int query(int rt,int l,int r,int L,int R){
    if(l>=L&&r<=R){
        return siz[rt];
    }
    int mid=l+r>>1;
    int ans=0;
    if(L<=mid)ans+=query(ls[rt],l,mid,L,R);
    if(R>mid)ans+=query(rs[rt],mid+1,r,L,R);
    return ans;
}

int pos[N*30];
int main(){
    work();
    sort(e+1,e+1+cnt);
    int pre=0;

    rep(i,1,cnt){
        int ar=e[i].r;
        if(pos[e[i].k]){
            if(pos[e[i].k]<e[i].l){
                //printf("rt %d, pos %d -1\n",ar,pos[e[i].k]);
                insert(pre,rt[ar]=++cntTree,1,n,pos[e[i].k],-1);
                pre=rt[ar];
                pos[e[i].k]=e[i].l;

                //printf("rt %d, pos %d +1\n",ar,e[i].l);
                insert(pre,rt[ar]=++cntTree,1,n,e[i].l,1);
                pre=rt[ar];
                pos[e[i].k]=e[i].l;
            }
        }
        else{
            //printf("rt %d, pos %d +1\n",ar,e[i].l);
            insert(pre,rt[ar]=++cntTree,1,n,e[i].l,1);
            pre=rt[ar];
            pos[e[i].k]=e[i].l;
        }
    }
    int q=rd;
    int last=0;
    while(q--){
        int l=rd,r=rd;
        l=(l^last)%n+1;
        r=(r^last)%n+1;
        if(l>r)swap(l,r);
        last=query(rt[r],1,n,l,r);
        printf("%d\n",last);
    }
    return 0;
}

/*_________________________________________________________end*/

你可能感兴趣的:(数据结构)