Hdu 5147 Sequence II(树状数字 or 线段树 + 输入外挂 前缀和+后缀和)

题意:

给定1~n的一个排列 用A[]数组保存,问有多少下标(a,b,c,d)四元组满足:

a

解析:

题目中n的范围是50000,O(n^2) 复杂度肯定超时。那么这题明显考察的是log2(n)的算法,对于这题可以用线段树或者树状数组,同时要用到输入外挂,不然会超时。

思路(参考别人做法)

枚举c的位置,那么每一次枚举中的方法数为 1~c-1 中(a,b)的个数 乘以 c~n中(c,d)的个数。累加起来即为答案。

1~c-1中(a,b)的个数相当于枚举b的位置,然后计算出b前面有多少数比A[b]小,该值要保存下来,下一次枚举c的时候,该值再加上c-1前面有多少比a[c-1]小的数即为当前情况下1~c-1中(a,b)的个数,也就是b=c-1的时候,因为枚举b之前的情况已经算过了。

因为当算c时只是算出 比c小的方法数,这个只是满足比c小的二元组的个数。而c前面的二元组也要计算在内,所以要加上c前面的比c小的个数。

用树状数组来做。本题n范围50000,而且每个数都不相同很关键。所以我们就开辟n个位置,一开始每个位置都是0,其实每个位置不是0就是1,因为每个数只有一个。

比如 1 3 2 4 5

一开始 c数组 0 0 0 0 0

先统计,再输入,因为计算a[i]前面有多少比它小的数,不包括它自己,而树状数组计算和的时候,要包括它自己。

i=1,树状数组求和前缀和 pre[1]=0 ,

此时0 0 0 0 0, 输入1,变为 1 0 0 0 0

i=2,a[2]=3,要看 3前面有多少个数,也就是看c数组的3个位置前面有多少个1,1代表已经输入,

发现1 0 0 0 0前三个数只有一个1,也就是pre[2]=1 (输入的第二个数之前只有1个比它小的),输入3以后,c数组变为 1 0 1 0 0

i=3, a[3]= 2, 要看2前面有多少个数,也就是看c数组前2个位置前面有多少个1,发现10100前两个数中只有一个1,也就是pre[3]=1

前缀和可以算出来,那么后缀和 = num2[i] = n-i-a[i]+num[i]+1;

AC代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 5e4 + 10;
int n;
ll C[N], a[N];
inline void read(ll &x) {
    int flag = 0;
    x = 0;
    char c = getchar();
    if(c == ‘-‘)
        flag = 1;
    while(c < ‘0‘ || c > ‘9‘) {
        if(c == ‘-‘)
            flag = 1;
        c = getchar();
    }
    while(c >= ‘0‘ && c <= ‘9‘)
        x = x * 10 + c - ‘0‘, c = getchar();
    if(flag) x = -x;
}
inline int lowbit(int x) {
    return x & (-x);
}
int query(int x) {
    int ret = 0;
    while(x > 0) {
        ret += C[x];
        x -= lowbit(x);
    }
    return ret;
}
void add(int x, int d) {
    while(x <= n) {
        C[x] += d;
        x += lowbit(x);
    }
}
int num[N] , num2[N];
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        ll ans = 0, pre = 0;
        scanf("%d", &n);
        memset(C, 0, sizeof(C));
        for(int i = 1; i <= n; i++) {
            read(a[i]);
            num[i] = query(a[i]); //计算a[i]之前比a[i]小的数字
            num2[i] = n-i-a[i]+num[i]+1; //计算a[i]之后比a[i]大的数字
            add(a[i], 1);
            ans += pre * num2[i];
            pre += num[i];
        }
        printf("%lld\n", ans);
    }
    return 0;
}
时间: 2025-01-02 23:27:31

Hdu 5147 Sequence II(树状数字 or 线段树 + 输入外挂 前缀和+后缀和)的相关文章

hdu 5147 Sequence II(树状数组)

题目链接:hdu 5147 Sequence II 预处理每个位置作为b和c可以组成的对数,然后枚举b的位置计算. #include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int maxn = 50005; int N, arr[maxn], fenw[maxn], lef[maxn], rig[maxn]; #d

HDU 1566 Color the ball(树状数组or线段树)

Color the ball Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11387    Accepted Submission(s): 5680 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"

HDU 1166 敌兵布阵 (我的树状数组加线段树点修改模板)

思路:本题因为是点修改,所以我们可以用线段树或者是树状数组了.线段树的基本操作我在我的代码中会具体体现,关键是要理解下面这幅图,具体的思想大家可以去看看其他的资料 线段树AC代码: #include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; #define N 50005 int num

浅谈二维中的树状数组与线段树

一般来说,树状数组可以实现的东西线段树均可胜任,实际应用中也是如此.但是在二维中,线段树的操作变得太过复杂,更新子矩阵时第一维的lazy标记更是麻烦到不行. 但是树状数组在某些询问中又无法胜任,如最值等不符合区间减法的询问.此时就需要根据线段树与树状数组的优缺点来选择了. 做一下基本操作的对比,如下图. 因为线段树为自上向下更新,从而可以使用lazy标记使得矩阵的更新变的高校起来,几个不足就是代码长,代码长和代码长. 对于将将矩阵内元素变为某个值,因为树状数组自下向上更新,且要满足区间加法等限制

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

树状数组与线段树

一:树状数组 树状数组是对一个数组改变某个元素和求和比较实用的数据结构.两中操作都是O(logn). 需求:有时候我们需要频繁地求数组的前k项和或者求数组从小标i到j的和,这样每次最坏情况下的时间复杂度就会为O(N),这样效率太低了.而树状数组主要就是为了解决这样一个问题.树状数组在求和及修改都可以在O(lgN)时间内完成. 树状数组需要额外维护一个数组,我们设为C[N],原数组为A[N], 其中每个元素C[i]表示A[i-2^k+1]到A[i]的和,这里k是i在二进制时末尾0的个数.注意通过位

[BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】

题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过128MB = = 嗯就是这样,代码长度= =我写了260行......Debug了n小时= = 代码 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #in

[BZOJ 1901] Dynamic Rankings 【树状数组套线段树 || 线段树套线段树】

题目链接:BZOJ - 1901 题目分析 树状数组套线段树或线段树套线段树都可以解决这道题. 第一层是区间,第二层是权值. 空间复杂度和时间复杂度均为 O(n log n). 代码 树状数组套线段树 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <algorithm> #include <cstring> usin

BZOJ 3295 [Cqoi2011]动态逆序对 树状数组套线段树

题意:链接 方法:树状数组套线段树 解析: 这题基本上写的都是什么CDQ点分治,主席树之类的,然而这我都并不会,所以写了一发平衡树套线段树想卡时卡过去,然而我并没有得逞,T的不要不要的,这里用平衡树套线段树的方法参见我的题解:排队.这道题比那道更要简单. 然后我就打算弃坑了~不过看140142做这道题做的热火朝天的,还是打算回来做一下,yy下树状数组套线段树,然后去看hz的题解,只看懂他写理论部分了,代码部分不知所云,所以还是还是得yy.引用理论部分. 删除某个数,只要统计它之前还存在的比它大的