Acwing 241. 楼兰图腾 树状数组 线段树(无lazy标记)

https://www.acwing.com/problem/content/243/

在完成了分配任务之后,西部 314 来到了楼兰古城的西部。
相传很久以前这片土地上(比楼兰古城还早)生活着两个部落,一个部落崇拜尖刀(V),一个部落崇拜铁锹(∧),
他们分别用 V 和 ∧ 的形状来代表各自部落的图腾。
西部 314在楼兰古城的下面发现了一幅巨大的壁画,壁画上被标记出了 n个点,
经测量发现这 n个点的水平位置和竖直位置是两两不同的。
西部 314认为这幅壁画所包含的信息与这 n 个点的相对位置有关,
因此不妨设坐标分别为 (1,y1),(2,y2),…,(n,yn),其中 y1∼yn 是 1到 n 的一个排列。
西部 314打算研究这幅壁画中包含着多少个图腾。
如果三个点 (i,yi),(j,yj),(k,yk) 满足 1i<j<k≤n 且 yi>yj,yj<yk,则称这三个点构成 V 图腾;
如果三个点 (i,yi),(j,yj),(k,yk) 满足 1i<j<k≤n且 yi<yj,yj>yk,则称这三个点构成 ∧ 图腾;
西部 314想知道,这 n个点中两个部落图腾的数目。
因此,你需要编写一个程序来求出 V 的个数和 ∧ 的个数。

输入格式
第一行一个数 n。
第二行是 n个数,分别代表 y1,y2,…,yn。

输出格式
两个数,中间用空格隔开,依次为 V 的个数和 ∧ 的个数。

数据范围
对于所有数据,n≤200000,且输出答案不会超过 int64。
y1∼yn 是 1 到 n 的一个排列。

输入样例:
5
1 5 3 2 4
输出样例:
3 4

1 树状数组解法
记录每个点左边和右边比他大的和小的点的个数
根据乘法原理,两边相乘就构成三角的个数

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 200010;
int tr[N],a[N];
int n;

int lowbit(int x){
    return x&-x;
}

void add(int x,int c){
    for(int i=x;i<=n;i+= lowbit(i)){
        tr[i]+=c;
    }
}

int sum(int x){
    int res = 0;
    for(int i =x;i;i-=lowbit(i)){
        res += tr[i];
    }

    return res;
}


long long gr[N],low[N];

int main(){
    cin >> n;
    for(int i=1;i<=n;i++){
        cin >> a[i]; int y = a[i];
        gr[i] = (sum(n)-sum(y));
        low[i] = sum(y-1);
        add(y,1);
    }
    memset(tr,0,sizeof tr);
    long long res1=0,res2=0;
    for(int i=n;i>=1;i--){
        int y = a[i];
        res1 += gr[i]*1ll*(sum(n)-sum(y));
        res2 += low[i]*1ll*sum(y-1);
        add(y,1);
    }

    cout  << res1<<" " <<res2<<endl;

    return 0;
}

2 线段树 没有lazy标记解法
同树状数组一样的思路 不过代码就麻烦一些

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 200010;
struct NODE{
    int l,r;
    int cnt;
}tr[N*4];
int n;
int a[N];

void pushup(struct NODE& u,struct NODE& l,struct NODE& r){
    u.cnt =l.cnt+r.cnt;
}

void pushup(int u){
    pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}


void add(int u,int l,int r,int a){
    if(l<=tr[u].l && r>= tr[u].r){
        tr[u].cnt += a;
    }else{
        int mid = tr[u].l + tr[u].r >>1;
        if(l <=mid) add(u<<1,l,r,a);
        if(r>mid) add(u<<1|1,l,r,a);
        pushup(u);
    }
}

int query(int u,int l,int r){
    if(l<=tr[u].l && r >= tr[u].r) return tr[u].cnt;

    int mid = tr[u].l+tr[u].r >> 1;
    long long v = 0;
    if(l<=mid) v = query(u<<1,l,r);
    if(r>mid) v+= query(u<<1|1,l,r);
    
    return v;
}

void build(int u,int l,int r){
    if(l==r){
        tr[u] = {l,r,0};
        return;
    }
    tr[u].l = l; tr[u].r = r;
    int mid = l+r>>1;
    build(u<<1,l,mid); build(u<<1|1,mid+1,r);
    pushup(u);
}

int gr[N],low[N];

int main(){
    cin >>n;
    build(1,1,n);
    for(int i=1;i<=n;i++){
        cin >>a[i];
        gr[i] = query(1,a[i]+1,n);
        low[i]=query(1,1,a[i]-1);
        add(1,a[i],a[i],1 );
    }

    long long res1 =0,res2=0;
     build(1,1,n);
     for(int i =n;i>=1;i--){
        res1 += 1ll*gr[i]*query(1,a[i]+1,n);
        res2 += 1ll*low[i]*query(1,1,a[i]-1);
        add(1,a[i],a[i],1 );
     }
    
    cout << res1<<" " <<res2<<endl;

    return 0;
}

我的视频题解空间

posted on   itdef  阅读(6)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2022-06-05 papamelon 242. 二分图判定(挑战程序设计竞赛)
2022-06-05 papamelon 218. 01背包问题(挑战程序设计竞赛)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示