HDU5196--DZY Loves Inversions 树状数组 逆序数

题意查询给定[L, R]区间内 逆序对数 ==k的子区间的个数。

我们只需要求出 子区间小于等于k的个数和小于等于k-1的个数,然后相减就得出答案了。

对于i(1in),我们计算ri表示[i,ri]的逆序对数小于等于K,且ri的值最大。(ri对应代码中的cnt数组)

显然ri单调不降,我们可以通过用两个指针扫一遍,利用树状数组计算出r数组。

对于每个询问L,R,我们要计算的是i=LR[min(R,ri)i+1]

由于ri具有单调性,那我们直接在上面二分即可,然后记一个前缀和(s数组)。

  1 #include <set>
  2 #include <map>
  3 #include <cmath>
  4 #include <ctime>
  5 #include <queue>
  6 #include <stack>
  7 #include <cstdio>
  8 #include <string>
  9 #include <vector>
 10 #include <cstdlib>
 11 #include <cstring>
 12 #include <iostream>
 13 #include <algorithm>
 14 using namespace std;
 15 typedef unsigned long long ull;
 16 typedef long long ll;
 17 const int inf = 0x3f3f3f3f;
 18 const double eps = 1e-8;
 19 const int maxn = 1e5+100;
 20 int n, q, tot, a[maxn];
 21 ll k, vec[maxn];
 22 int lowbit (int x)
 23 {
 24     return x &  -x;
 25 }
 26 ll arr[maxn];
 27 int M ;
 28 void modify(int x, int d)
 29 {
 30     while (x < M)
 31     {
 32         arr[x] += d;
 33         x += lowbit(x);
 34     }
 35 }
 36 ll sum(int x)
 37 {
 38     ll res = 0;
 39     while (x)
 40     {
 41         res += arr[x];
 42         x -= lowbit(x);
 43     }
 44     return res;
 45 }
 46 ll cnt[2][maxn];
 47 ll s[2][maxn];
 48 ll solve (int L, int R, ll x, int w)                 // 小于等于x的数量
 49 {
 50     if (x < 0)
 51         return 0;
 52     //int tmp = 0;
 53     int tmp = lower_bound(cnt[w]+L, cnt[w]+R+1, (ll)R) - cnt[w];
 54     while (tmp >= R+1)                           //  cnt数组中所有数都小于 R时,,得到的tmp是大于R+1的
 55         tmp--;
 56     while (cnt[w][tmp] > R && tmp >= L)
 57        tmp--;
 58     if (tmp < L)
 59         return (ll)R*(ll)(R-L+1) - (ll)(L+R)*(ll)(R-L+1)/2;
 60     return s[w][tmp] - s[w][L-1] - (ll)(R-tmp)*(ll)(R+tmp+1)/2+ (ll)(R-tmp)*(ll)R;
 61 }
 62 int main()
 63 {
 64     #ifndef ONLINE_JUDGE
 65         freopen("in.txt","r",stdin);
 66         freopen("out.txt", "w", stdout);
 67     #endif
 68     while (~scanf ("%d%d%I64d", &n, &q, &k))
 69     {
 70         M = n + 10;
 71         memset(arr, 0, sizeof (arr));
 72         memset(cnt, 0, sizeof (cnt));
 73         memset(s, 0, sizeof(s));
 74         for (int i = 0; i < n; i++)
 75         {
 76             scanf ("%I64d", vec+i);
 77             a[i] = vec[i];
 78         }
 79         sort (vec, vec+n);
 80         tot = unique(vec, vec+n) - vec;
 81         for (int i = 0; i < n; i++)
 82         {
 83             a[i] = lower_bound(vec, vec+tot, a[i]) - vec + 2;           //离散化
 84         }
 85         ll res = 0;
 86         //小于等于k
 87         for (int i = 0, j = 0; i < n; i++)
 88         {
 89             for ( ; j < n && res <= k; j++)
 90             {
 91                 res += (j - i) - sum(a[j]);
 92                 modify(a[j], 1);
 93             }
 94             if (res >= k)
 95                 cnt[1][i] = (res > k ? max(0,j -1-1): j-1) ;           // -1是因为 j先加了一下, 才跳出 循环的
 96             else
 97                 cnt[1][i] = j-1-1;
 98             s[1][i] = s[1][i-1] + cnt[1][i] - (i);
 99             modify(a[i], -1);
100             res -= sum(a[i]-1);
101         }
102 
103         //小于等于k-1
104         res = 0;
105         for (int i = 0, j = 0; i < n; i++)
106         {
107             for ( ; j < n && res <= (k-1); j++)
108             {
109                 res += (j-i) - sum(a[j]);
110                 modify(a[j], 1);
111             }
112             if (res >= k-1)
113                 cnt[0][i] = (res > (k-1) ? max(j-1-1,0) : j-1);
114             else
115                 cnt[0][i] = j-1-1;
116 
117             s[0][i] = s[0][i-1] + cnt[0][i] - (i);
118             modify(a[i], -1);
119             res -= sum(a[i]-1);
120         }
121         for (int i = 0; i < q; i++)
122         {
123             int u, v;
124             scanf ("%d%d", &u, &v);
125             u--, v--;
126             if (u > v)
127                 swap(u, v);
128             ll ans1 = solve(u, v, k, 1);
129             ll ans2 = solve(u, v, k-1, 0);
130             if (k == 0)
131                 ans1 += (v-u+1);                            // 考虑形如[a, a]的区间
132             printf("%I64d\n", ans1-ans2 );
133         }
134     }
135     return 0;
136 }

 

转载于:https://www.cnblogs.com/oneshot/p/4384639.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值