践踏(思维转换 + 树状数组 + 离散化)

题目连接:
https://ac.nowcoder.com/acm/contest/26896/1002
这个题好难哇,不会…看完题解只能说太妙啦!

分析

参考题解:
https://blog.nowcoder.net/n/5d56166fc2804d55b4f43f32d2aa42d6?f=comment

主要思想是能把多个数的查询转换成单点查询;
通过%k意义下就能做到固定x的值;
那么我们的区间也可以%k做到固定;
此时我们只需要查询x这个点有多少区间就行;

维护区间个数的话我们可以用 差分 + 树状数组维护前缀和 来搞;
由于树状数组维护的是类似下标的,所以要从1开始,即%k后要+1

这里注意当k=0时要单独考虑;
思想也是一样的,这里就不用通过%k去固定x了,x本身就是固定的;
我们只用维护一下区间就行,这里维护区间就要离散化一下了(因为1e9);
显然区间需要是有序的,所以我们要保序 (排序去重+二分)
由于需要离散化,因此我们需要离线操作…

代码

int n,k;
vector<int> v;	//离散化
struct node
{
	int op,l,r,x;
}no[maxn];

int tr[maxn];

void update(int x,int v)
{
	while(x < maxn)
	{
		tr[x] += v;
		x += lowbit(x);
	}
}

int query(int x)
{
	int ans = 0;
	while(x)
	{
		ans += tr[x];
		x -= lowbit(x);
	}
	return ans;
}

int getid(int x)	//二分
{
	return lower_bound(v.begin(),v.end(),x) - v.begin() + 1;
}

int main()
{
	iire(n,k);
	
	if(n == 0)
	{
		cout<<"fafa\n";
		return 0;
	}
	
	for(int i=1;i<=n;i++)
	{
		int op,l,r,x;
		ire(op);
		
		if(op != 3)
		{
			iire(l,r);
			v.push_back(l);
			v.push_back(r);
			no[i] = {op,l,r};
		}
		else
		{
			ire(x);
			v.push_back(x);
			no[i].op = 3;
			no[i].x = x;
		}
	}
	
	//离散化
	sort(v.begin(),v.end());
	v.erase(unique(v.begin(),v.end()),v.end());
	
	if(k == 0)
	{
		for(int i=1;i<=n;i++)
		{
			int op = no[i].op;
			int l = getid(no[i].l);
			int r = getid(no[i].r);
			int x = getid(no[i].x);
			
			if(op == 1)
			{
				update(l,1);
				update(r+1,-1);
			}
			else if(op == 2)
			{
				update(l,-1);
				update(r+1,1);
			}
			else
			{
				cout<<query(x)<<'\n';
			}
		}
	}
	else
	{
		for(int i=1;i<=n;i++)
		{
			int op = no[i].op;
			int l = no[i].l;
			int r = no[i].r;
			int x = no[i].x;
			
			if(op == 1)
			{
				if(r - l + 1 >= k)	//此时x无论为多少值都能覆盖这个区间
				{
					update(1,1);
					update(k+1,-1);
				}
				else
				{
					l = (l % k + 1);
					r = (r % k + 1);
					if(l <= r)
					{
						update(l,1);
						update(r+1,-1);
					}
					else	//这里想不明白的考虑这个例子[8,11],k=5 => [4,2]
					{
						update(1,1);
						update(r+1,-1);
						update(l,1);
						update(k+1,-1);
					}
				}
			}
			else if(op == 2)
			{
				if(r - l + 1 >= k)
				{
					update(1,-1);
					update(k+1,1);
				}
				else
				{
					l = (l % k + 1);
					r = (r % k + 1);
					if(l <= r)
					{
						update(l,-1);
						update(r+1,1);
					}
					else	//这里想不明白的考虑这个例子[8,11],k=5 => [4,2]
					{
						update(1,-1);
						update(r+1,1);
						update(l,-1);
						update(k+1,1);
					}
				}
			}
			else
			{
				x = (x % k + 1);
				cout<<query(x)<<'\n';
			}
		}
	}
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值