zoukankan      html  css  js  c++  java
  • 树状数组之图腾计数

    题目

    思路

    开始码了遍暴力

    #include<bits/stdc++.h>
    using namespace std;
    int a[200000+10];
    int cnt1,cnt2;
    int main(){
    	int n;scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	if(n<3){
    		printf("0
    ");
    		printf("0
    ");
    		return 0;
    	}
    	for(int i=1;i<=n-2;i++){
    		for(int j=i+1;j<=n-1;j++){
    			for(int k=j+1;k<=n;k++){
    				if(a[i]<a[j]&&a[k]<a[j])cnt2++;
    				if(a[i]>a[j]&&a[k]>a[j])cnt1++;
    			}
    		}
    	}
    	
    	printf("%d ",cnt1);
    	printf("%d
    ",cnt2);
    	return 0;
    }
    
    

    不用想肯定是错的,所以就去想其他思路

    • 对于某一个柱子,可以求它左右延伸的距离

    于是我想到了单调栈,和-->这道题,不过显然这样是不对的,经过一番折腾后,get到了如下思路

    • 对于某一个柱子,维护其左右两边比其矮的个数,左右相乘为以该柱子为中心上凸的情况数
    • 对于某一个柱子,维护其左右两边比其高的个数,左右相乘为以该柱子为中心下凸的情况数
      那么怎么维护呢?我想到了树状数组求逆序对,和-->某道题有一丝丝像。

    以上凸情况为例

    • -->维护p数组存放高度i对应的位置,因为高度是不重复的
    • -->维护树状数组以位置为下标,表示该位置下的柱子是否存在(方便查询个数)
    • -->对高度从小到大循环,确保在当前柱子放之前,比其小的柱子已经放完
    • -->查询其左右两边的柱子个数(一定是比当前柱子小的,因为大的还没放)
    • -->将当前柱子放入相应位置
    for(int i=1;i<=n;i++){
    		long long l=ask(p[i]-1);
    		long long r=ask(n)-ask(p[i]);
    		ans1+=l*r;
    		add(p[i],1);
    	}
    

    下凸情况反过来跑一遍就行(记得清空树状数组)

    总代码

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=200000+10;
    long long a[maxn],c[maxn],p[maxn];
    long long n;
    inline long long read(){
    	long long s=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')ch=getchar();
    	while(ch>='0'&& ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    	return s;
    }
    void add(int x,int y){
    	for(;x<=n;x+=x&-x)c[x]+=y;
    }
    int ask(int x){
    	int ans=0;
    	for(;x;x-=x&-x)ans+=c[x];
    	return ans;
    }
    int main(){
    	n=read();
    	for(int i=1;i<=n;i++){
    		a[i]=read();
    		p[a[i]]=i;
    	}
    	if(n<3){
    		printf("0
    ");
    		printf("0
    ");
    		return 0;
    	}
    	long long ans1=0,ans2=0;
    	for(int i=1;i<=n;i++){
    		long long l=ask(p[i]-1);
    		long long r=ask(n)-ask(p[i]);
    		ans1+=l*r;
    		add(p[i],1);
    	}
    	memset(c,0,sizeof(c));
    	for(int i=n;i>=1;i--){
    		long long l=(long long)ask(p[i]-1);
    		long long r=(long long)(ask(n)-ask(p[i]));
    		ans2+=l*r;
    		add(p[i],1);
    	}
    	printf("%lld %lld
    ",ans2,ans1);
    	return 0;
    }
    
    
  • 相关阅读:
    上下界网络流——概念解析与快速入门(待修改)
    maomao的现在与未来
    exgcd证明和最基础应用
    快速入门Splay
    luogu 2515
    bzoj 1996
    *51nod 1409
    51nod 1412
    51nod 1503
    51nod 1020
  • 原文地址:https://www.cnblogs.com/soda-ma/p/13262803.html
Copyright © 2011-2022 走看看