hdu——ping pong

这道题可以简化为:

一条直线上有N个点,每一个点有一个weight权值,对于情况:从左到右有三个点a, b, c满足a >= b > =c或者a <= b < =c。满足情况的有多少种可能。 (因为满足每个点均不相同,所以也可以是 a > b > c 或者a < b < c);

constraints:3 <= N <= 20000;

考虑dp的方法,每一个点存放两个数字,up和down分别表示到这个点升和降的个数。那么有:

1.c[i]存放权值

2. dp[i] = up[i] + down[i];

3.up[i] = ∑(up[j])  (j < i, c[i] >= c[j]); down[i] = ∑ (down[j]) ( j < i, c[j] >= c[i]);

具体复杂度O(n^2) ≈ 400000000理论上会超时,考虑优化的方法。

其实对于某一个数字c[i]来说,如果满足c[i] < c[j]而且,i < j;如果有k > j,而且c[k] > c[j],那么一定有c[k] > c[i],就做了重复的判断和加减。所以还可以更加优化复杂度。

对于a < b < c,上面考虑的是枚举c的情况,如果枚举b,可以将问题拆分成 a < b 和 b < c两个问题;

例如a[i] < a[6] < a[j],那么就需要统计:a[0] ~a[5]中满足a[i] < a[6]的个数和a[7] ~a[n]中满足a[j] > a[6]的个数。

分别考虑一下几种数据结构:

1. 线段树。(这个不太懂为什么可以。)

2.树状数组。(树状数组a数组里存放的是每一个点的权值,的c数组里存放的是满足条件的个数);

3. 块状链表。

4.平衡二叉树。


首先学习一下标程:

#include <iostream>

#include <time.h>

using namespace std;

const int maxm = 100000;//A[i]的最大值

const int maxn = 20000;//N的范围

int lb[maxm + 100], b[maxm + 100], c[maxm + 100];//lb为树状数组的辅助函数,b数组为保存了树状数组的统计信息

int a[maxn + 100];

int g[maxn + 100], h[maxn + 100];


int calc(int b[maxm], int k)//树状数组统计个数的函数{

int i , ans = 0;

for(i = k; i > 0; i -= lb[i]) ans += b[i];

return ans;

}


void ins(int b[maxm], int k){

int;

for(i = k; i <= maxm; i += lb[i] ) b[i]++;

}


int main() {

int cn, n, i;

_int64 ans;

scanf("%d", &cn);

for(i = 1; i <= maxm;  i++) lb[i] = i & (-i );

while(cn--) {

scanf("%d", &n);

for(i = 0; i < n; i++) scanf("%d", &a[i]);

memset(b, 0, sizeof(b));

memset(c, 0, sizeof(c));

for(i = 0; i < n; i++) {

g[i] = cal(b, a[i] - 1);

ins(b, a[i]);

for(i = n - 1; i >= 0; i--) {

h[i] = calc(c, a[i] - 1);

ins(c, a[i]);

}

ans = 0;

for(i = 0; i < n; i++) 

ans += (_int64)g[i] * (_int64)(n - i - 1 - h[i]) + (_int64)(i - g[i]) * (_int64)h[i];

printf("%d", ans);

}

return 0;

}


看完程序我发现树状数组存的是标志位的信息,也就是说树状数组最后的那一行是不存在的。

ac代码:

#include <cstdio>
#include <iostream>
using namespace std;

typedef long long LL;

const int maxm = 100000;//保存A[i]的信息
const int maxn = 20000;//保存数组c给定序列的信息

int c[maxn + 100];//保存题目中的序列
int u[maxm + 100];//保存树状数组的信息,up
int d[maxm + 100];//保存树状数组的信息,down
int lb[maxm + 100];//保存lowbit的信息
int sum_u[maxn + 100];//保存up和的信息
int sum_d[maxn + 100];//保存down和的信息

void insert(int a[maxm], int k) {
    int i = k;
    while(i <= maxm) {
        a[i] += 1;
        i += lb[i];
    }
}

//数组从1开始
int query(int a[maxm], int k) {
    int i = k, ret = 0;
    while(i > 0) {
        ret += a[i];
        i -= lb[i];
    }
    return ret;
}

void debug() {
    for(int i = 1; i <= 10; i++) {
        cout << i << " " << sum_u[i] << " " << sum_d[i] << " " << endl;
    }
    cout << endl;
    //for()
}


int main() {
    int T, n;
    long long ans;
    for(int i = 1; i <= maxm; i++) lb[i] = i & (-i);
    scanf("%d", &T);
    while(T--) {
        ans = 0;
        scanf("%d", &n);
        for(int i = 0; i < n; i++) scanf("%d", &c[i]);
        memset(u, 0, sizeof(u));
        memset(d, 0, sizeof(d));
        for(int i = 0; i < n; i++) {
            sum_u[i] = query(u, c[i] - 1);//从左往右数比c[i]小的个数
            insert(u, c[i]);
        }
        for(int i = n - 1; i >= 0; i--) {
            sum_d[i] = query(d, c[i] - 1);//从右往左数比c[i]小的个数
            insert(d, c[i]);
        }
        //cout << ans << endl;
        //debug();
        for(int i = 0; i < n; i++) {
            ans += (LL)sum_u[i] * (n - i - 1 - sum_d[i]);
            ans += (LL)(i - sum_u[i]) * sum_d[i];
            //debug_begin
            //int tp = (LL)sum_u[i] * (n - i - 1 - sum_d[i]) + (LL)(i - sum_d[i]) * sum_d[i];
            //cout << " &&& " << i << " " <<  (LL)sum_u[i] * (n - i - 1 - sum_d[i]) << " " << (LL)(i - sum_d[i]) * sum_d[i] << endl;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值