• 树状数组 逆序对


    用大点节点C来表示小节点和的信息,这样我们可以进行快速的查询。

    树状数组可以进行的操作:1.单点修改 2.区间查询 时间复杂度O(logn)

    方法:新建一个数组 ci = a( i- lowbit(i)) + 1 到 ai 的 和

    具体实现是二进制, 自己可以去查阅一下

    查询操作:

    1. ll query(int x)//求 1 ... x的和
    2. {
    3. ll s = 0;
    4. for (; x; x -= x & (-x)) s += c[x];
    5. return s;
    6. }

    修改操作:

    1. void modify(int x, ll s)// a[x] += s;
    2. {
    3. for (; x <= n; x += x & (-x))
    4. c[x] += s;
    5. }

    ok,现在我们可以来做例题了!

    P3374 【模板】树状数组 1 

    本题要求我们实行单点修改 和 区间查询, 注意不是[1, x]这个区间,而是[l, r]这个区间。由于我们的query查询的都是[1, x]这种的区间,根据前缀和的思想,用query(y) - query(x - 1)即可求出区间[x, y]的ans了

    1. #include
    2. using namespace std;
    3. #define x first
    4. #define y second
    5. #define endl '\n'
    6. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    7. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    8. #define pb push_back
    9. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    10. typedef long long ll;
    11. typedef pair<int,int> PII;
    12. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    13. const int mod = 1e9+7;
    14. const int N = 500010;
    15. int a[N];
    16. ll c[N];
    17. int n, q;
    18. ll lowbit(ll x) { return x & (- x); }
    19. ll query(int x)// 1 ... x的和
    20. {
    21. ll s = 0;
    22. for (; x; x -= lowbit(x)) s += c[x];
    23. return s;
    24. }
    25. void modify(int x, ll s)// a[x] += s;
    26. {
    27. for (; x <= n; x += lowbit(x)) c[x] += s;
    28. }
    29. int main()
    30. {
    31. IOS;
    32. cin >> n >> q;
    33. for (int i = 1; i <= n; i ++ )
    34. {
    35. cin >> a[i];
    36. modify(i, a[i]);//给i的位置加上a[i], 对树状数组来说 一开始是0麻
    37. }
    38. for (int i = 0; i < q; i ++ )
    39. {
    40. int ty;
    41. cin >> ty;
    42. if(ty == 1)
    43. {
    44. int x, d;
    45. cin >> x >> d;
    46. modify(x, d);//改成d, 那么 增量就是d - a[x];
    47. a[x] += d;
    48. }
    49. else
    50. {
    51. int x, y; cin >> x >> y;
    52. cout << query(y) - query(x - 1) << endl;
    53. }
    54. }
    55. return 0;
    56. }

     P3368 【模板】树状数组 2

    本题要求我们实现区间加, 也就是区间修改和单点查询。因为其实我们的树状数组是对前缀进行query, 所以我们进行单点查询的时候,我们怎么样才能得到一个单点的值呢???

    假设我们是对差分数组进行建立树状数组, 那么我们查询出来的就不是一个前缀和了, 而是1个点的值了。这个应该很好理解

    同时,对差分数组建立了树状数组也满足了区间修改这个操作。我们知道差分数组中,修改[l, r]这个区间, 只需要q[l] +, q[r + 1] -, 即可。这里同理。 

    1. #include
    2. using namespace std;
    3. #define x first
    4. #define y second
    5. #define endl '\n'
    6. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    7. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    8. #define pb push_back
    9. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    10. typedef long long ll;
    11. typedef pair<int,int> PII;
    12. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    13. const int mod = 1e9+7;
    14. const int N = 500010;
    15. int a[N];
    16. ll c[N];
    17. int n, q;
    18. ll lowbit(ll x) { return x & (- x); }
    19. ll query(int x)// 1 ... x的和
    20. {
    21. ll s = 0;
    22. for (; x; x -= lowbit(x)) s += c[x];
    23. return s;
    24. }
    25. void modify(int x, ll s)// a[x] += s;
    26. {
    27. for (; x <= n; x += lowbit(x)) c[x] += s;
    28. }
    29. int main()
    30. {
    31. IOS;
    32. cin >> n >> q;
    33. for (int i = 1; i <= n; i ++ )
    34. {
    35. cin >> a[i];
    36. modify(i, a[i] - a[i - 1]);//给i的位置加上a[i], 对树状数组来说 一开始是0麻
    37. }
    38. for (int i = 0; i < q; i ++ )
    39. {
    40. int ty;
    41. cin >> ty;
    42. if(ty == 1)
    43. {
    44. ll l, r, d;
    45. cin >> l >> r >> d;
    46. modify(l, d);
    47. modify(r + 1, -d);
    48. }
    49. else
    50. {
    51. int x;
    52. cin >> x;
    53. cout << query(x) << endl;
    54. }
    55. }
    56. return 0;
    57. }

    P1908 逆序对 

    问题本来是要求 i < j 且 ai > aj这个静态问题变成动态问题!    类似扫描线

    for j = 1 ~ n
        统计 a1 ~ aj-1 里  > a[j]

    问题转化为:我们要维护一个数据结构,数据结构d里面存了a1~aj-1,我们要统计d里有多少个数字大于a[j],做完将aj加入
    2.权值开树状数组

    现在数据结构D:1.加入一个数    2.统计>aj的数

    那么什么叫对权值开树状数组, 比如加入了一个数字x, 那么就d[x] += 1;
    那么当比如问多少个大于aj, 就是统计aj后面1的个数 ans += D[j]; 后缀和查询
    那么查后缀我们可以将ai -> n + 1 - ai,那么后缀和就变成了前缀和
    等于将1-n反转成n-1,这样就实现了后缀和变成了前缀和

    PS:我们代码没有离散化哦! 过不了

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. using namespace std;
    8. #define x first
    9. #define y second
    10. #define endl '\n'
    11. #define rep(i,a,n) for (int i = a; i < n; i ++ )
    12. #define repn(i,a,n) for (int i = a; i <= n; i ++ )
    13. #define pb push_back
    14. #define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
    15. typedef long long ll;
    16. typedef pair<int,int> PII;
    17. ll gcd(ll a,ll b) { return b ? gcd(b,a % b) : a; }
    18. const int mod = 1e9+7;
    19. const int N = 500010;
    20. ll lowbit(int x) { return x & (-x);}
    21. int a[N];
    22. ll c[N];
    23. int n;
    24. ll ans;
    25. ll query(int x)
    26. {
    27. ll s = 0;
    28. for (; x; x -= lowbit(x))
    29. s += c[x];
    30. return s;
    31. }
    32. void modify(int x, ll s)
    33. {
    34. for (; x <= n; x += lowbit(x)) c[x] += s;
    35. }
    36. int main()
    37. {
    38. IOS;
    39. cin >> n;
    40. for (int i = 1; i <= n; i ++ )
    41. {
    42. cin >> a[i];
    43. a[i] = n + 1 - a[i];
    44. }
    45. for (int i = 1; i <= n; i ++ )
    46. {
    47. ans += query(a[i]);
    48. modify(a[i], 1);
    49. }
    50. cout << ans << endl;
    51. return 0;
    52. }

     PS:贴一份别人的正确代码

    1. #include
    2. using namespace std;
    3. #define int long long
    4. const int N = 5e5+10;
    5. int a[N], c[N];
    6. vector<int> v;//离散化
    7. map<int, int> p;
    8. int n;
    9. int query(int x)
    10. {
    11. int res = 0;
    12. for(; x ;x -= x & (-x)) res += c[x];
    13. return res;
    14. }
    15. void insert(int x)
    16. {
    17. for(;x <= n;x += x&(-x)) c[x] ++;
    18. }
    19. signed main()
    20. {
    21. cin >> n;
    22. for(int i = 1; i <= n; i ++)
    23. {
    24. cin >> a[i];
    25. v.push_back(a[i]);
    26. }
    27. // 离散化
    28. sort(v.begin(),v.end());
    29. v.erase(unique(v.begin(),v.end()),v.end());
    30. for(int i = 0; i < v.size(); i ++)
    31. {
    32. p[v[i]] = i + 1;
    33. }
    34. int ans = 0;
    35. for(int i = 1; i <= n; i ++)
    36. {
    37. insert(p[a[i]]);
    38. // 累加结果即可
    39. ans += i - query(p[a[i]]);
    40. }
    41. cout << ans << endl;
    42. return 0;
    43. }
    44. /*
    45. i - query(a[i]) 可以得到比a[i]大的数的个数。
    46. query(a[i]) - mp[a[i]] 可以得到比a[i]小的个数。
    47. 这里mp[a[i]]表示离散化之后a[i]有几个。
    48. */

     

  • 相关阅读:
    3分钟告诉你如何成为一名黑客|零基础到黑客入门指南,你只需要掌握这五点能力
    国庆在家没事干?教大家用Python做一个任何视频都能看的软件, 当然,只能看正经的
    SQL 数据更新
    数据结构-线性表
    解读下SWD协议以及其应用
    Docker存储目录迁移
    Go与数据库:NoSQL数据库的应用
    centos通过docker快速搭建bWAPP
    Java Integer parseInt(String s, int radix)方法具有什么功能呢?
    【Neo4j系列】Neo4j概念简介及整合SpringBoot
  • 原文地址:https://blog.csdn.net/m0_61837058/article/details/127579382