题目:luogu2184.
题目大意:一个长度为
n
n
n的序列,每次操作为询问区间内不同的数的数量或给一段区间加入一个不同于之前所有数的数.
1
≤
n
≤
1
0
5
1\leq n\leq 10^5
1≤n≤105,操作数
≤
1
0
5
\leq 10^5
≤105.
那么这道题其实稍微一想就会是一棵线段树,但是这样得浪费大量空间和时间,很容易TLE或MLE.
考虑用维护每一个位置 i i i前面区间的左端点数量 c l i cl_i cli和右端点数量 c r i cr_i cri,显然一个询问区间 [ l , r ] [l,r] [l,r]的答案为 c l r − c r l cl_r-cr_l clr−crl.
然后问题就转化成为维护两个支持单点查询区间修改的树状数组了.
那么代码如下:
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
int n,m;
class tree{
public:
int c[200000];
inline int lowbit(int k);
inline void build(int l,int r);
inline void add(int x,int num,int n);
inline int query(int l,int r);
};
inline int tree::lowbit(int k){
return k&-k;
}
inline void tree::build(int l,int r){
for (int i=0;i<200000;i++)
c[i]=0;
}
inline void tree::add(int x,int num,int n){
while (x<=n){
c[x]+=num;
x+=lowbit(x);
}
}
inline int tree::query(int l,int r){
l--;
int lsum=0,rsum=0;
while (l){
lsum+=c[l];
l-=lowbit(l);
}
while (r){
rsum+=c[r];
r-=lowbit(r);
}
return rsum-lsum;
}
tree tr1,tr2;
inline void into(){
}
inline void work(){
scanf("%d%d",&n,&m);
int c,x,y;
tr1.build(1,n);
tr2.build(1,n);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&c,&x,&y);
if (c==1) tr1.add(x,1,n),tr2.add(y,1,n);
else printf("%d\n",tr1.query(1,y)-tr2.query(1,x-1));
}
}
inline void outo(){
}
int main(){
for (int i=1;i<=1;i++){
into();
work();
outo();
}
return 0;
}