StkOvflow

STACK OVERFLOW!

一言(ヒトコト)

学OI后悔三年
不学OI后悔一辈子/呲牙
——zn

AcWing241. 楼兰图腾

传送门

题目大意

1.给你一个序列,让你统计三个数i,j,ki<j<k),当这三个数满足ai>ajaj<ak的对数
2.同样地统计i,j,k(i<j<k),求满足ai<aj,aj>aki,j,k对数

解题思路

我们已经知道如何利用树状数组求出一个数的后面有多少个数比它小。所以我们可以采取这种算法:
1.对于^字符,我们需要的其实是:
a.找出每个数i的前面比它小的字符数,记作l[i]
b.找出每个数i后面比它大的字符数,记作r[i]
然后根据,当一个数num被当做分界点j的时候,此时第一个数i的选法有l[num]种(只要比num小且在num的前面就行),第三个数k的选法有r[num]种(只要比num大而且在num的后面就行),所以以num为分界点时,方案有l[num]×r[num],而在一个序列中,每个数都可以作为分界点,所以枚举每个数,用resA统计
2.同样地对于字符V,我们也这样统计。

解题代码

这里代码放两份,一份是规矩扫描,另一份是边扫描第二次边统计。

代码1
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using LL = long long;

const int N = 2e5 + 10;
int n, a[N], Al[N], Bl[N], tr[N], Ar[N], Br[N];

void add(int x, int v) 
{
    for (; x <= n; x += x & -x) tr[x] += v;
}

int ask(int x) 
{
    int res = 0;
    for (; x; x -= x & -x) res += tr[x];
    return res;
}

int main() 
{
    scanf("%d", &n);
    
    for (int i = 1; i <= n; i ++ ) 
        scanf("%d", &a[i]);
        
    for (int i = 1; i <= n; i ++ ) 
    {
        int j = a[i];
        Al[j] += ask(j - 1), Ar[j] += ask(n) - ask(j); //Al是一个数前面比它小的,Ar是一个数前面比它大的
        add(j, 1);
    }
    
    memset(tr, 0, sizeof tr);
    
    LL resA = 0, resV = 0;
    for (int i = n; i; i -- ) 
    {
        int j = a[i];
        Br[j] += ask(j - 1), Bl[j] += ask(n) - ask(j); //Br是后面比它小的,Bl是后面比它大的
        add(j, 1);
    }
    
    for (int i = 1; i <= n; i ++ ) 
    {
        int j = a[i];
        resA += (LL) Al[j] * Br[j];
        resV += (LL) Ar[j] * Bl[j];
    }
    
    printf("%lld %lld\n", resV, resA);
    
    return 0;
}

代码2

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
using LL = long long;

const int N = 2e5 + 10;
int n, a[N], low[N], big[N], tr[N];

void add(int x, int v) 
{
    for (; x <= n; x += x & -x) tr[x] += v;
}

int ask(int x) 
{
    int res = 0;
    for (; x; x -= x & -x) res += tr[x];
    return res;
}

int main() 
{
    scanf("%d", &n);
    
    for (int i = 1; i <= n; i ++ ) 
        scanf("%d", &a[i]);
        
    for (int i = 1; i <= n; i ++ ) 
    {
        int j = a[i];
        low[j] += ask(j - 1), big[j] += ask(n) - ask(j);
        add(j, 1);
    }
    
    memset(tr, 0, sizeof tr);
    
    LL resA = 0, resV = 0;
    for (int i = n; i; i -- ) 
    {
        int j = a[i];
        resA += (LL) low[j] * ask(j - 1);
        resV += (LL) big[j] * (ask(n) - ask(j));
        add(j, 1);
    }
    printf("%lld %lld\n", resV, resA);
    
    return 0;
}

AC

posted @   StkOvflow  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示