牛客国庆训练 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 
 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 }
View Code

 

你可能感兴趣的:(牛客国庆训练 H.千万别用树套树)