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

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

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

对于i(1≤i≤n),我们计算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 }
时间: 2024-07-31 20:29:49

HDU5196--DZY Loves Inversions 树状数组 逆序数的相关文章

HDU 5196 DZY Loves Inversions(树状数组,二分)

这题之前CC做过类似的,思路如官方题解. 代码: #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int N = 100005; typedef long long ll; int n, m; ll q; struct Hash { int v, id; void read(int id) { scanf("%d", &

hdu2838Cow Sorting(树状数组+逆序数)

题目链接:点击打开链接 题意描述:给定一个长度为100000的数组,每个元素范围在1~100000,且互不相同,交换其中的任意两个数需要花费的代价为两个数之和.问如何交换使数组有序,花费的代价最小? 解题思路: 1.显然我们知道,要使一个数组有序至少交换的次数(即必须要交换的次数)为数组中的逆序数 2.由于数组的长度比较大所以我们可以通过树状数组来统计结果 此处需要两个树状数组 第一个:记录小于等于某个值的元素的个数 第二个:记录小于等于某个值的元素的和 代码: #include <cstdio

HDU 4911 (树状数组+逆序数)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4911 题目大意:最多可以交换K次,就最小逆序对数 解题思路: 逆序数定理,当逆序对数大于0时,若ak<ak+1,那么交换后逆序对数+1,反之-1. 设原始序列最小逆序对数=cnt 那么,交换K次的最小逆序对数max(0,cnt-k) 在求原始序列最小逆序对数上,朴素暴力复杂度O(n^2)不可取 有以下两种O(nlogn)的方法: ①排序内计算: 主要是利用归并排序内的特性,即相邻两个归并序列逆序情

树状数组+逆序数与顺序数——HDU 2492

对应HDU题目:点击打开链接 Ping pong Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Status Description N(3N20000) ping pong players live along a west-east street(consider the street as a line segment). Each player has a unique skil

POJ3067 树状数组+逆序数

设两线段为(x1,y1) ,(x2,y2), 若使两线段相交,需使x1<x2&&y1>y2||x1>x2&&y1<y2. 那么本题就变得很简单了,对东边点x从小到大排序,当x相等时对西边点y从小到大排序,每插入一条线段,就求一下逆序对数.总和即为答案. 代码如下: 1 #include <stdio.h> 2 #include <string.h> 3 #include <algorithm> 4 #define

hdu 5193 分块 树状数组 逆序对

题意: 给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除.求每次修改后逆序对的个数. 限制: 1 <= n,m <= 20000; 1 <= ai <= n 思路: 插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个. 外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力.查找权值个数时,使用树状数组比较方便.内层通过树状数组维护权值. 每次更新即

Bzoj 2789: [Poi2012]Letters 树状数组,逆序对

2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Status][Discuss] Description 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. Input 第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度

【树状数组逆序对】USACO.2011JAN-Above the median

[题意] 给出一串数字,问中位数大于等于X的连续子串有几个.(这里如果有偶数个数,定义为偏大的那一个而非中间取平均) [思路] 下面的数据规模也小于原题,所以要改成__int64才行.没找到测试数据,自己编的几组.简单来说读入每个数,大于等于中位数设为1,小于设为-1,前i个数的和建立一个树状数组,求逆序对. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorit

Poj 2299 - Ultra-QuickSort 离散化,树状数组,逆序对

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 52306   Accepted: 19194 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin