刷题记录:牛客NC21125践踏

该文章描述了一道编程竞赛中的五星难题,涉及到线段树、离散化和差分概念。题目要求在数轴上处理线段操作,并查询特定点x加上整数倍k后落入多少区间内。对于k=0的情况,可以通过差分和前缀和优化;对于k不为0的情况,需要考虑取模运算和线段长度对结果的影响。文章提供了相应的代码实现思路。
摘要由CSDN通过智能技术生成

传送门:牛客

题目描述:

首先给定一个定值k,支持如下操作(在数轴上)
1. 加入一条线段[l,r]
2. 删除一条已经存在的线段
3. 给定x,问有多少个区间包含x+kt,其中t是一个整数变量,即t ∈ Z
比如说当x=2,k=3的时候,区间[7,10]是应该算入答案的,因为x+2k=8,且7 ≤ 8 ≤ 10
如果n=0,那么你只需要输出一行"fafa"然后结束程序即可(注意不输出双引号)
输入:
10 7
1 3393 14151
3 13229
1 3427 18356
1 7602 20138
1 8566 28714
1 1076 32552
2 3427 18356
2 8566 28714
3 10962
1 387 7783
输出:
1
3

一道线段树+离散化+差分的题目,综合性和代码量都比较大,不愧是牛客上的5星题

首先我们看一下我们的题目,会发现k是给定的,并且应有两种情况需要考虑

先考虑 k = 0 k=0 k=0的情况,此时我们会发现我们的题目变成了问有多少个区间包含 x x x.对于这种问题我们首先肯定会有一种比较朴素的想法,就是每有一段线段,就将线段里的所有点加上上1.但是这种想法显然会超时,所以我们需要优化一下.此时就需要我们的差分思想了,我们可以将线段的开头加上1,结尾减少1,这样差分的思想就很好的解决了我们的问题.维护完线段之后再跑一个前缀和即可.但是我们此时的问题不是离线的,而是在线的.所以我们需要使用线段树代替朴素的数组来维护我们的差分.

但是,我们发现我们的 l , r l,r l,r非常大,达到了 1 e 9 1e9 1e9,此时我们的线段树显然无法维护这么大的数据.所以我们需要进行离散化操作.具体如何进行离散化可以观看后文代码

然后我们考虑 k k k不为0的情况,此时我们看一下我们的式子,会发现我们的 t t t只要是整数就可.这意味什么呢,意味着我们的 y y y等于 x % k + N ∗ k , N ≥ 0 x\%k+N*k,N\geq0 x%k+Nk,N0,那么此时计算我们的所有区间里有多少值包括了 y y y.我们可以将这个问题进行一个简单的转化,也就是求所有区间的点对 k k k取模后有多少值和 x % k x\%k x%k相等.

当线段的长度大于等于 k k k时,显然我们的线段会包括 y y y,因为大于等于 K K K意味我们的余数肯定充满了K;当我们的线段长度小于 k k k,我们可以将线段的左右端点对 k k k取模,得到 L , R L,R L,R,如果此时我们的 L < = R L<=R L<=R(注意此时相等的情况肯定是原 l l l= r r r,因为如果不是这种情况,我们的线段的长度肯定大于等于 k k k了),那么我们直接 a d d ( L , 1 ) , a d d ( R + 1 , − 1 ) add(L,1),add(R+1,-1) add(L,1),add(R+1,1)即可.当我们的 L > R L>R L>R时,我们需要 a d d ( L , 1 ) , a d d ( K + 1 , − 1 ) , a d d ( 1 , 1 ) , a d d ( R + 1 , − 1 ) add(L,1),add(K+1,-1),add(1,1),add(R+1,-1) add(L,1),add(K+1,1),add(1,1),add(R+1,1).

但是此时会存在一个问题,就是我们的数据的初始状态是0,但是我们取模也存在0这种贡献,所以会发生错误.此时我们可以选择将所有取模后的结果+1来避免产生0

下面是具体的代码部分:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define	int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int n,k;
struct Segment_tree{
	int l,r,sum;
}tree[maxn*4];
void build(int l,int r,int rt) {
	tree[rt].l=l;tree[rt].r=r;
	if(l==r) {
		tree[rt].sum=0;
		return ;
	}
	int mid=(l+r)>>1;
	build(lson);build(rson);
	tree[rt].sum=tree[ls].sum+tree[rs].sum;
}
void update(int rt,int pos,int v) {
	if(tree[rt].l==pos&&tree[rt].r==pos) {
		tree[rt].sum+=v;
		return ;
	}
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(pos<=mid) update(ls,pos,v);
	else update(rs,pos,v);
	tree[rt].sum=tree[ls].sum+tree[rs].sum;
}
int query(int l,int r,int rt) {
	if(tree[rt].l==l&&tree[rt].r==r) {
		return tree[rt].sum;
	}
	int mid=(tree[rt].l+tree[rt].r)>>1;
	if(r<=mid) return query(l,r,ls);
	else if(l>mid) return query(l,r,rs);
	else return query(l,mid,ls)+query(mid+1,r,rs);
}
vector<int>v;
struct O{
	int opt,l,r;
}opt[maxn];
int getid(int x) {
//加一是为了将0位置变成1,也就是整体往后移一格
	return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
int main() {
	n=read();k=read();
	if(n==0) {
		puts("fafa");
		return 0;
	}
	for(int i=1;i<=n;i++) {
		int Opt=read();
		if(Opt==1) {
			int l=read(),r=read();
			opt[i]={Opt,l,r};
			v.push_back(l);v.push_back(r);
		}
		else if(Opt==2) {
			int l=read(),r=read();
			opt[i]={Opt,l,r};
			v.push_back(l);v.push_back(r);
		}
		else {
			int x=read();
			opt[i]={Opt,x,x};
			v.push_back(x);
		}
	}
	if(k==0) {
	//进行离散化操作
		sort(v.begin(),v.end());
		v.erase(unique(v.begin(),v.end()),v.end());
		int Size=v.size();
	//离散化结束
		build(1,Size*2,1);//有点懒,懒得算具体数值了,直接取2倍了,下同
		for(int i=1;i<=n;i++) {
			if(opt[i].opt==1) {
				int l=getid(opt[i].l),r=getid(opt[i].r);
				update(1,l,1);update(1,r+1,-1);
			}
			else if(opt[i].opt==2) {
				int l=getid(opt[i].l),r=getid(opt[i].r);
				update(1,l,-1);update(1,r+1,1);
			}
			else {
				int x=getid(opt[i].l);
				printf("%d\n",query(1,x,1));
			}
		}
	}
	else {
		build(1,k*2,1);
		for(int i=1;i<=n;i++) {
			if(opt[i].opt==1) {
				if(opt[i].r-opt[i].l+1>=k) {
					update(1,1,1);update(1,k+1,-1);
				}
				else {
				//取模后加一
					int l=opt[i].l%k+1,r=opt[i].r%k+1;
					if(l<r) {
						update(1,l,1);update(1,r+1,-1);
					}
					else {
						update(1,l,1);update(1,k+1,-1);
						update(1,1,1);update(1,r+1,-1);
					}
				}
			}
			else if(opt[i].opt==2) {
				if(opt[i].r-opt[i].l+1>=k) {
					update(1,1,-1);update(1,k+1,1);
				}
				else {
					int l=opt[i].l%k+1,r=opt[i].r%k+1;
					if(l<r) {
						update(1,l,-1);update(1,r+1,1);
					}
					else {
						update(1,l,-1);update(1,k+1,1);
						update(1,1,-1);update(1,r+1,1);
					}
				}
			}
			else {
				int x=opt[i].l%k+1;
				printf("%d\n",query(1,x,1));
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值