首页 > 其他分享 >NC14522 珂朵莉的数列

NC14522 珂朵莉的数列

时间:2023-05-01 17:11:06浏览次数:48  
标签:return 数列 int 个数 sum NC14522 朵莉 uniq 逆序

题目链接

题目

题目描述

珂朵莉给了你一个序列,有 \(\frac{n\times(n+1)}2\) 个子区间,求出她们各自的逆序对个数,然后加起来输出

输入描述

第一行一个数 n 表示这个序列 a 的长度之后一行 n 个数,第i个数表示ai

输出描述

输出一行一个数表示答案

示例1

输入

10
1 10 8 5 6 2 3 9 4 7

输出

270

示例2

输入

20
6 0 4 5 8 8 0 6 6 1 0 4 6 6 0 0 7 2 0 5

输出

3481

备注

对于100%的数据,n <=1000000 ,0 <= 序列中每个数 <= 1000000000

题解

知识点:线段树,离散化,枚举。

这道题在经典的逆序对问题上加了一点点东西。

对于经典逆序对问题,是通过从左到右枚举每个数,对于某个数求出在它左边且大于它的数的个数,利用线段树或树状数组的权值和解决的。在这道题,我们可以借用这个思路。

假设数组 \(a\) 有 \(n\) 个数,考虑一对逆序对 \((x,y),x<y\) 产生的贡献,显然是包含其的区间个数 \(x(n-y+1)\) ,其中 \(a_x\) 贡献了 \(x\) 个有效点, \(a_y\) 贡献了 \(n - y + 1\) 个有效点。

我们固定右端点 \(y\) ,那么产生的贡献为 \((n-y+1)\displaystyle \sum_{x<y,a_x > a_y}x\) ,只需要求出在它左边且大于它的数贡献的有效点的个数和,只需要将逆序对问题中的权值从个数替换为贡献的有效点个数即可。

因此,我们从左到右枚举每个数(枚举顺序维护了在左边的偏序关系),用权值线段树维护出现过的数的权值(有效点个数),随后只需要询问大于当前数的权值和(线段树维护了大于的偏序关系),即为 \(\displaystyle \sum_{x<y , a_x>a_y}x\) 。

另外,本题数据范围要离散化,结果超过 long long ,要用 __int128_t

时间复杂度 \(O(n \log n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

template<class T>
struct Discretization {
    vector<T> uniq;
    Discretization() {}
    Discretization(const vector<T> &src) { init(src); }
    void init(const vector<T> &src) {
        uniq = src;
        sort(uniq.begin() + 1, uniq.end());
        uniq.erase(unique(uniq.begin() + 1, uniq.end()), uniq.end());
    }
    int get(T x) { return lower_bound(uniq.begin() + 1, uniq.end(), x) - uniq.begin(); }
};

struct T {
    ll sum;
    static T e() { return { 0 }; }
    friend T operator+(const T &a, const T &b) { return { a.sum + b.sum }; }
};
struct F {
    int add;
    T operator()(const T &x) { return { x.sum + add }; }
};

template<class T, class F>
class SegmentTree {
    int n;
    vector<T> node;

    void update(int rt, int l, int r, int x, F f) {
        if (r < x || x < l) return;
        if (l == r) return node[rt] = f(node[rt]), void();
        int mid = l + r >> 1;
        update(rt << 1, l, mid, x, f);
        update(rt << 1 | 1, mid + 1, r, x, f);
        node[rt] = node[rt << 1] + node[rt << 1 | 1];
    }

    T query(int rt, int l, int r, int x, int y) {
        if (r < x || y < l) return T::e();
        if (x <= l && r <= y) return node[rt];
        int mid = l + r >> 1;
        return query(rt << 1, l, mid, x, y) + query(rt << 1 | 1, mid + 1, r, x, y);
    }

public:
    SegmentTree(int _n = 0) { init(_n); }

    void init(int _n) {
        n = _n;
        node.assign(n << 2, T::e());
    }

    void update(int x, F f) { update(1, 1, n, x, f); }

    T query(int x, int y) { return query(1, 1, n, x, y); }
};

template<class T>
inline void write(T x) {
    if (x < 0) { putchar('-');x = -x; }
    if (x >= 10) write(x / 10);
    putchar(x % 10 + '0');
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    vector<int> a(n + 1);
    for (int i = 1;i <= n;i++) cin >> a[i];

    Discretization<int> dc(a);
    int n_rk = dc.uniq.size() - 1;
    SegmentTree<T, F> sgt(n_rk);

    // 假设i < j,若有逆序对(i,j),那么贡献为i*(n-j+1)
    // 从左往右枚举逆序对右侧的数字a[i],则可以累加左侧数字,即[1,i-1],>a[i]的数的贡献,最后乘n-i+1即可
    __int128_t ans = 0;
    for (int i = 1;i <= n;i++) {
        ans += sgt.query(dc.get(a[i]) + 1, n_rk).sum * (n - i + 1);
        sgt.update(dc.get(a[i]), { i });
    }

    write(ans);
    puts("");
    return 0;
}

标签:return,数列,int,个数,sum,NC14522,朵莉,uniq,逆序
From: https://www.cnblogs.com/BlankYang/p/17366713.html

相关文章

  • 使用数学归纳法证明斐波那契数列通项公式
    使用数学归纳法证明斐波那契数列通项公式:\(F_{n}=\dfrac{\phi^{n}-\hat{\phi}^{n}}{\sqrt{5}}\)定义已知斐波那契数列\(F\)定义为:\[F_{n}=\begin{cases}0,n=0\\n,n=1\\F_{n-1}+F_{n-2},i\ge2\end{cases}\]\(\phi\)和......
  • 斐波那契数列第n项
    importjava.util.Scanner;publicclassMain{ publicstaticvoidmain(String[]args){ Scannersc=newScanner(System.in); intn=sc.nextInt(); inta=1,b=1,i=1; while(i<n){ intc=a+b; a=b; ......
  • Fib数列的递推
     矩阵快速幂 #include<iostream>#include<cmath>#include<algorithm>usingnamespacestd;#defineN2intmod;#defineintlonglongstructmatrix{ inta[N+2][N+2];};matrixm1;intn;voidinit_(matrix&a......
  • Python 斐波那契数列
    概念:斐波那契数列又称黄金分割数列,即:1,1,2,3,5,8,13,21,…,这个数列前两项都是1,从第3项开始,每一项都等于前两项之和。随着数列的增加,前一项与后一项的比值逼近0.6180339887这个黄金分割系数 code:deffiblist(input):fib=[1,1]#第一和第二项固定为值为1......
  • 连续数列和问题
    关于7的迷题Description给你n个数,分别是a[1],a[2],...,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],...,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。FormatInput第一行给出数字N,1≤N≤50,000接下来N个数字,值在[0…1,000,000]......
  • [NOI2005] 维护数列
    总体思路其实跟用线段树维护区间最大字段和差不多,不过唯一麻烦的地方在于要算上自己。然后我们可以开一个队列来回收那些被delete的点,这样可以节省空间,特别需要注意的是release的时候,标记什么的一定记得清空。本来insert我是直接一个个merge的,这样就会导致特别慢,因此我们可以借......
  • #yyds干货盘点# LeetCode程序员面试金典:外观数列
    题目:给定一个正整数n,输出外观数列的第n项。「外观数列」是一个整数序列,从数字1开始,序列中的每一项都是对前一项的描述。你可以将其视作是由递归公式定义的数字字符串序列:countAndSay(1)="1"countAndSay(n)是对countAndSay(n-1)的描述,然后转换成另一个数字字符串。前五项......
  • 剑指 Offer 10- I. 斐波那契数列
     分析:偷个懒,上次做的一样的题代码:1classSolution(object):2deffib(self,n):3"""4:typen:int5:rtype:int6"""7ifn<2:8returnn9f=[0foriinra......
  • 兔子数列
    有一对兔子,从出生后的第三个月起,每个月生一对小兔子,假设所有的兔子都不死亡,30个月后会有多少兔子?分析:此问题是数学中著名的兔子数列问题(斐波那契数列),1,1,2,3,5.........其通式为:n=n-1+n-2;由此可以写出代码。#include<stdio.h>intmain(){ inti,f,f1=1,f2=1; printf("%d,%d",f1,......
  • 斐波那契数列
    斐波那契数列 公式:F(n)=F(n-1)+F(n-2)  步骤: 1、初始化:第0项为0,第1项为1if(n<=1){  returnn;} 2、设置参数,确保第二项也为1intres=0;inta=0;intb=1; 3、从2开始循环到n,把自己的值赋给下一项for(inti=2;i<=n;i++){  res=a+......