牛客国庆训练 H.千万别用树套树
链接https://ac.nowcoder.com/acm/contest/1108/H
国庆队内训练的题,当时还完全没思路,就没补。现在会树状数组了,倒是能想一想,不过网上题解好多用线段树传数组的?我看不太懂,觉得还是树状数组维护方便多了。
建两颗BIT维护分别维护左右端点。
由于对于第二种询问操作,ri-li<=2,带来了极大的便利。直接用已插入的总数-左端点大于l的线段个数-右端点小于r的线段个数即可,但有种情况需要特判。
而我网上看的题解,大部分都忽略了一个问题,就是线段退化成点的问题,样例里也给出了这样的情况,显然对于操作2,ri-li==2时,这样退化的线段会被减去两次,但令人惊讶的是数据出水了,导致不考虑这个问题也能AC。。。。。。
比如说
3 3
1 2 2
1 2 2
2 1 3
这组数据网上大部分的题解的代码会输出-2,中间这个点被减了两遍。
应对也很简单,再用一个数组在插入时,保存l==r的单独的点即可,询问时在加上。
1 #include <bits/stdc++.h> 2 #define debug(x) cout << #x << ": " << x << endl 3 using namespace std; 4 typedef long long ll; 5 const int MAXN=1e5+7; 6 const int INF=0x3f3f3f3f; 7 const int MOD=1e9+7; 8 9 int sol[MAXN]; 10 11 struct BIT 12 { 13 int c[MAXN]; 14 int lowbit(int x){return x&(-x);} 15 void add(int i,int x) 16 { 17 while(i<MAXN) 18 { 19 c[i]+=x; 20 i+=lowbit(i); 21 } 22 } 23 ll sum(int i) 24 { 25 ll res=0; 26 while(i) 27 { 28 res+=c[i]; 29 i-=lowbit(i); 30 } 31 return res; 32 } 33 }L,R; 34 35 int main() 36 { 37 int n,q; 38 while(~scanf("%d%d",&n,&q)) 39 { 40 memset(L.c,0,sizeof(L.c)); 41 memset(R.c,0,sizeof(R.c)); 42 memset(sol,0,sizeof(sol)); 43 int cnt=0; 44 int ans=0; 45 while(q--) 46 { 47 int op,l,r; 48 scanf("%d%d%d",&op,&l,&r); 49 if(op==1) 50 { 51 if(l==r) sol[l]++; 52 L.add(l,1); 53 R.add(r,1); 54 cnt++; 55 } 56 else 57 { 58 int t1=cnt-L.sum(l); 59 int t2=R.sum(r-1); 60 ans=cnt-t1-t2; 61 if(r-l==2) ans+=sol[l+1]; 62 printf("%d\n",ans); 63 } 64 } 65 } 66 return 0; 67 }