//树状数组 千万别用树套树

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43880084/article/details/102020449

LINK->牛客

链接:https://ac.nowcoder.com/acm/contest/1108/H
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
Special Judge, 64bit IO Format: %lld
题目描述
Bobo 精通数据结构!他想维护一个线段的集合 S。初始时,S 为空。他会依次进行 q 次操作,操作有 2 种。

  • 类型 1:给出 l, r,向集合 S 中插入线段 [l, r].
  • 类型 2:给出 l, r,询问满足 [x,y]∈S[x, y] \in S[x,y]∈S 且 x≤l≤r≤yx \leq l \leq r \leq yx≤l≤r≤y 的线段 [x, y] 数量。
    帮 Bobo 求出每次询问的答案。
    输入描述:

输入文件包含多组数据,请处理到文件结束。
每组数据的第一行包含 2 个整数 n 和 q. 其中 n 表示操作中 r 的最大值。
接下来 q 行中的第 i 行包含 3 个整数 ti,li,rit_i, l_i, r_iti​,li​,ri​,表示第 i 个操作属于类型 tit_iti​,对应的参数是 lil_ili​ 和 rir_iri​.

输出描述:

对于每个类型 2 的询问,输出 1 个整数表示对应的数量。

示例1
输入
复制

1 2
1 1 1
2 1 1
4 4
1 1 4
2 2 3
1 1 4
2 2 3

输出
复制

1
1
2

备注:

  • 1≤n,q≤1051 \leq n, q \leq 10^51≤n,q≤105
  • ti∈{1,2}t_i \in {1, 2}ti​∈{1,2}
  • 1≤li≤ri≤n1 \leq l_i \leq r_i \leq n1≤li​≤ri​≤n
  • 对于 ti=2t_i = 2ti​=2 的操作,ri−li≤2r_i - l_i \leq 2ri​−li​≤2 成立。
  • 数据组数不超过 10.

1A还是很开心的~~所以什么是树套树o(╥﹏╥)o

想法在后面啊

#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int c[N],num[N],bz[N],n;
inline int lowbit(int x)//单点修改,区间查询 (差分思想 
{
	return x&(-x);
}
inline void modify(int pos,int x)
{
	for(;pos<=n;pos+=lowbit(pos))
	{
		c[pos]+=x;
	}
}
int query(int pos)
{
 int res=0;
	for(;pos;pos-=lowbit(pos))
	{
		res+=c[pos];
	}return res;
 } 
int main()
{
	int m;
	
	while(~scanf("%d%d",&n,&m))
	{
    for(int i=0;i<=n;i++) c[i]=num[i]=bz[i]=0;
	int op,x,y;
	while(m--)
	{
		scanf("%d",&op);
		if(op==1)
		{
		scanf("%d%d",&x,&y);
		modify(x,1);
		modify(y+1,-1);
		if(x!=y) num[x]++;
		else bz[x]++;	
		}else
		{
			scanf("%d%d",&x,&y);
			int ans;
			//cout<<query(x)<<" "<<query(y)<<endl;
			if(x==y) ans=query(x);
			else if(x==y-1) 
			{
				ans=query(y)-bz[y]-num[y];
			}
			else 
			{
				ans=query(y)-num[x+1]-num[y]-bz[y];
			}
            printf("%d\n",ans);
		}
    }	
    
	}
	
	
}


bz是作为重(chong)点的个数; num表示是这个点作为左端点的个数

  1. 设一线段的两个端点分别x ,y
  2. 树状数组在x端处理+1 ,y+1处理-1 那么对于x-y之间的线段的个数至少都为1 而对于后端点在y之后线段就是无效的(差分+前缀和)
  3. 对于x=y 就相当单点啦,就判断所以达到y的线段个数 那就是前缀和
  4. 对于y=x+1 那就是从达到y的线段个数中除去这个是重点以及左端点的个数
  5. 对于y=x+2 同样至少要是能达到y的线段 减去 y是重点或左端点的个数再减去中间的那个点作为左端点的个数 {所以现在能达到y的左端点至少是在x 开始的也就符合条件了}(这里不能减去中间点作为重点的个数因为重点根本就不在能达到y的线段里面啊)
  • 更简洁
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int c[N],bz[N],num[N],n,m;
inline int lowbit(int x)
{
	return x&(-x);
} 
void add(int x,int v)
{
	for(;x<=n;x+=lowbit(x)) c[x]+=v;
}
int sum(int x)
{
	int ans=0;
	for(;x;x-=lowbit(x)) ans+=c[x];
	return ans; 
}
int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		for(int i=0;i<=n;i++) c[i]=bz[i]=num[i]=0;
		while(m--)
		{
			int oo,a,b;scanf("%d%d%d",&oo,&a,&b);
			if(oo==1)
			{
				add(a,1);add(b+1,-1);
				if(a==b) bz[a]++;
				else num[a]++;
			}else 
			{
				if(a==b) printf("%d\n",sum(a));
				else if(a==b-1) printf("%d\n",sum(b)-num[b]-bz[b]);
				else printf("%d\n",sum(b)-bz[b]-num[b]-num[a+1]);
			}
		} 
	}
}

猜你喜欢

转载自blog.csdn.net/weixin_43880084/article/details/102020449