DTOJ#5014. 牛客的数列题

题目背景

题目背景纯粹为吐槽,如果不想看的可以直接翻到最后。

宫水三叶在牛客上做题,她想知道牛客上的题目有多难。

三叶先找到一道全场最多人通过的,被公认为水题的题目:

给定 n n n ,请求出下列式子的值。

∫ 0 1 ( x − x 2 ) n d x \int_{0}^{1} (x-x^2)^n dx 01(xx2)ndx

作为一个高中生,三叶并不会做这道题,并且认为它和 OI 的关系不大。

接着三叶看到了一道计数题,三叶认为这题和 OI 的关系比较大。

n + m n+m n+m 个点,其中前 n n n 个点为 x 1 , x 2 , … , x n x_1,x_2,\dots,x_n x1,x2,,xn,后 m m m 个点为 y 1 , y 2 , … , y m y_1,y_2,\dots,y_m y1,y2,,ym

给定序列 a a a ,已知对于 x i x_i xi 和前 a i a_i ai y y y 有边相连。

请计算生成树的方案。

三叶思考了一阵,尝试了动态规划,矩阵树定理和生成函数等多种方法,还是不会做。于是三叶点开了题解:

本题的答案是 [一个简单的式子] , 具体证明在 [一篇很长的论文] 中。

三叶认为这种论文题不应该出现在 OI 中,于是就点开了下一题:

平面几何是一种非常有趣的问题。比如下面两个问题:

例一:在圆 M M M 中有两条弦 A C AC AC N T NT NT M M M 到两条弦的距离相等。已知 ∠ M T N = 2 8 ∘ \angle MTN = 28 ^ \circ MTN=28,求 ∠ M A C \angle MAC MAC

例二:在 △ A D E \triangle ADE ADE 中, B B B A E AE AE 上, C C C A D AD AD 上,且 B C / / D E BC // DE BC//DE。若 A C = x − 3 , B E = 20 , A B = 16 , C D = x + 5 AC=x-3,BE=20,AB=16,CD=x+5 AC=x3,BE=20,AB=16,CD=x+5,求 x x x 的值。

现在输入一道平面几何问题,请编写程序解决它。

看到这道题 17 17 17 人提交 0 0 0 人通过,三叶想起了以前有一位爱出斗地主和麻将的出题人。

三叶不想在牛客上继续写题了,便发誓如果下一题还这么奇怪,就放弃写题。

于是三叶点开了下一题。

题目描述

给定一个长度为 n n n 的序列 a a a

定义 F ( l , r ) = a l & a l + 1 & ⋯ & a r F(l,r)=a_l \And a_{l+1} \And \dots \And a_r F(l,r)=al&al+1&&ar

其中 & \And & 为二进制下 AND 操作。

定义集合 S ( l , r ) = { F ( x , y ) ∣ m i n ( l , r ) ≤ x ≤ y ≤ m a x ( l , r ) } S(l,r)=\{F(x,y)|min(l,r)\le x\le y\le max(l,r)\} S(l,r)={F(x,y)min(l,r)xymax(l,r)}

给定 q q q 次询问,每次输出 S ( l , r ) S(l,r) S(l,r) 的大小(即集合中的元素个数)。

并且要求强制在线。方式如下:

l a s t a n s lastans lastans 为上一次的答案,初始为 0 0 0

输入 L , R L,R L,R l = ( L ⊕ l a s t a n s ) % n + 1 , r = ( R ⊕ l a s t a n s ) % n + 1 l=(L\oplus lastans)\%n+1,r=(R\oplus lastans)\%n+1 l=(Llastans)%n+1,r=(Rlastans)%n+1。其中 ⊕ \oplus 为二进制下 XOR 操作。

第一行两个整数 n , o p n,op n,op

接下来一行 n n n 个整数,表示序列 a a a

接下来一行一个整数 q q q ,表示询问数。

接下来 q q q 行,每行 2 2 2 个整数。

如果 o p = 0 op=0 op=0 ,则表示不要求强制在线。否则表示要求强制在线。

具体操作为当 o p = 0 op=0 op=0 时,在每一次询问前都把 l a s t a n s lastans lastans 赋值为 0 0 0 ,否则不做操作。

而输入仍然为 L , R L,R L,R l , r l,r l,r 的计算式仍为 l = ( L ⊕ l a s t a n s ) % n + 1 , r = ( R ⊕ l a s t a n s ) % n + 1 l=(L\oplus lastans)\%n+1,r=(R\oplus lastans)\%n+1 l=(Llastans)%n+1,r=(Rlastans)%n+1

共输出 q q q 行,第 i i i 行表示第 i i i 个询问的答案。

样例输入 1
5 1
15 14 13 11 7
3
2 5
2 5
2 5
样例输出 1
4
1
3
样例输入 2,3

见下发文件。

样例二满足 o p = 0 op=0 op=0 ,样例三满足 o p = 1 op=1 op=1

本题采用捆绑测试,每个子任务 10 10 10 分。

对于所有数据,满足 1 ≤ n ≤ 1 0 5 , 0 ≤ a i < 2 30 , 1 ≤ q ≤ 1 0 5 , 0 ≤ L , R < 2 30 , o p ∈ { 0 , 1 } 1\le n \le 10^5 ,0\le a_i < 2^{30},1\le q \le 10^5,0\le L,R < 2^{30},op\in\{0,1\} 1n105,0ai<230,1q105,0L,R<230,op{0,1}

子任务编号 n , q n,q n,q a i a_i ai o p op op
1 1 1 ≤ 1 0 2 \le 10^2 102 < 2 30 < 2^{30} <230 = 0 =0 =0
2 2 2 ≤ 1 0 2 \le 10^2 102 < 2 30 < 2^{30} <230 = 1 =1 =1
3 3 3 ≤ 2 × 1 0 3 \le 2\times 10^3 2×103 < 2 10 < 2^{10} <210 = 0 =0 =0
4 4 4 ≤ 2 × 1 0 3 \le 2\times 10^3 2×103 < 2 10 < 2^{10} <210 = 1 =1 =1
5 5 5 ≤ 2 × 1 0 3 \le 2\times 10^3 2×103 < 2 30 < 2^{30} <230 = 0 =0 =0
6 6 6 ≤ 2 × 1 0 3 \le 2\times 10^3 2×103 < 2 30 < 2^{30} <230 = 1 =1 =1
7 7 7 ≤ 1 0 5 \le 10^5 105 < 2 10 < 2^{10} <210 = 0 =0 =0
8 8 8 ≤ 1 0 5 \le 10^5 105 < 2 10 < 2^{10} <210 = 1 =1 =1
9 9 9 ≤ 1 0 5 \le 10^5 105 < 2 30 < 2^{30} <230 = 0 =0 =0
10 10 10 ≤ 1 0 5 \le 10^5 105 < 2 30 < 2^{30} <230 = 1 =1 =1

题解:
首先可以知道,对于每个固定的左端点,只有最多 l o g log log 个区间有用,因为对于与和改变的断点后面值相同的区间会包含它,不优。
现在只有 n l o g n nlogn nlogn 的区间。
然后再把包含关系去掉。
现在对于两个相交区间,采取区间差分即可。即新建一个区间,表示同时包含两个区间时贡献去重。

#include<bits/stdc++.h>
#define N 500005
#define A t[p].ls
#define B t[p].rs
using namespace std;
inline int read(){
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	return x*f;
}
int a[N],n;
struct seg{
	int ls,rs,sum;
}t[N<<4];
int tot;
inline int add(){
	t[++tot].ls=0,t[tot].rs=0,t[tot].sum=0;
	return tot;
}
inline void up(int p){
	t[p].sum=t[A].sum+t[B].sum;
}
int change(int p,int q,int l,int r,int to,int val){
	t[p]=t[q];
	if(l==r){
		t[p].sum+=val;
		return p;
	}
	int mid=(l+r)>>1;
	if(to<=mid)A=change(add(),t[q].ls,l,mid,to,val);
	else B=change(add(),t[q].rs,mid+1,r,to,val);
	up(p);
	return p;
}
int quer(int q,int p,int l,int r,int to){
	if(to<=l)return t[p].sum-t[q].sum;
	int mid=(l+r)>>1;
	int sum=quer(t[q].rs,t[p].rs,mid+1,r,to);
	if(to<=mid)sum+=quer(t[q].ls,t[p].ls,l,mid,to);
	return sum;
}
int nex[N][31];
struct node{
	int l,r,val,ad,b;
	inline node(int i=0,int j=0,int k=0,int h=0,int z=0){
		l=i,r=j,val=k,ad=h,b=z;
	}
}q[N*30*2];
bool cmp1(node a,node b){
    if(a.val==b.val){
	    if(a.l==b.l)return a.r>b.r;
	    return a.l<b.l;
	}
	return a.val<b.val;
}
//bool cmp2(node a,node b){if(a.l==b.l)return a.r>b.r;return a.l<b.l;}
bool cmp2(node a,node b){if(a.b==b.b)return a.l<b.l;return a.b<b.b;}
bool cmp3(node a,node b){if(a.b==b.b)return a.r<b.r;return a.b<b.b;}
int root[N],qt;
inline void test(int p){
	cout<<q[p].l<<" "<<q[p].r<<" "<<q[p].val<<" "<<q[p].ad<<" "<<q[p].b<<endl;
}
inline void qad(int i,int j,int k,int h,int z){
	q[++qt].l=i,q[qt].r=j,q[qt].val=k,q[qt].ad=h,q[qt].b=z;
}
int main(){
//	freopen("nowcoder3.in","r",stdin);
//	freopen("nowcoder.out","w",stdout);
	n=read();int op=read();
	for(int i=1;i<=n;++i)a[i]=read();
	for(int j=0;j<=30;++j){
		nex[n+1][j]=n+1;
		for(int i=n;i;--i){
			if((a[i]>>j)&1)nex[i][j]=nex[i+1][j];
			else nex[i][j]=i;
		}
	}
	for(int i=1;i<=n;++i){
		int val=(1ll<<31)-1,k=i,minn=n+1;
		for(int j=0;j<=30;++j){
			if(!((a[i]>>j)&1))val-=(1<<j);
			else minn=min(minn,nex[i][j]);
		}
		//cout<<i<<":"<<val<<endl;
		k=minn;
		qad(i,i,val,1,0);
		while(k!=n+1&&val){
			minn=n+1;
			
			for(int j=0;j<=30;++j){
				if((val>>j)&1)if(!((a[k]>>j)&1))val-=1<<j;
				if((val>>j)&1)minn=min(minn,nex[k][j]);
			}//cout<<k<<" "<<val<<endl;
			qad(i,k,val,1,0);
			k=minn;
			//cout<<i<<" "<<k<<" "<<val<<" "<<qt<<endl;
		}
		//cout<<endl;
	}
	//cout<<qt<<endl;
	sort(q+1,q+1+qt,cmp1);
	int pre=qt;
	//cout<<endl;
	for(int i=1,j;i<=pre;i=j+1){
		for(j=i;j+1<=qt&&(q[j+1].val==q[i].val);++j);
		int min_r=n+1;
		for(int k=j;k>=i;--k){
			if(q[k].r>=min_r)q[k].b=1;
			min_r=min(min_r,q[k].r);
		}
		sort(q+i,q+j+1,cmp2);
		for(int k=i+1;k<=j;++k){
			if(q[k].b)break;
			qad(q[k-1].l,q[k].r,q[k].val,-1,0);
			//test(qt);
		}
		//for(int k=i;k<=j;++k)test(k);
		//cout<<endl;
			
	}//cout<<1<<endl;
	
	sort(q+1,q+1+qt,cmp3);
	//cout<<qt<<endl;
	tot=-1;add();
	//cout<<qt<<endl;
	//cout<<endl;
	for(int i=1,j=1;i<=n;++i){
		if(q[j].b)break;
		//cout<<i<<":"<<endl;
		while(j<=qt&&q[j].r<=i&&q[j].b==0){
			if(root[i]){
			    root[i]=change(add(),root[i],1,n,q[j].l,q[j].ad);
			}else{
				root[i]=change(add(),root[i-1],1,n,q[j].l,q[j].ad);
			}
			//cout<<q[j].l<<" "<<q[j].r<<" "<<q[j].ad<<" "<<q[j].val<<endl;
			++j;
		}
		//cout<<i<<endl;
	}
	//cout<<1<<endl;
	int t=read(),la=0;
	//cout<<qt<<endl;
	//return 0;
	while(t--){
		int l=read(),r=read();
		if(op==0)la=0;
		l=(l^la)%n+1,r=(r^la)%n+1;
		if(l>r)swap(l,r);
		la=quer(root[l-1],root[r],1,n,l);
		//cout<<root[l-1]<<" "<<root[r]<<endl;
		printf("%d\n",la);
	}
	//node now(1,1,1,1,1);cout<<now.l<<" "<<now.r<<endl;
	return 0;
}
/*
5 1
15 14 13 11 7
1
2 5


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值