CDOJ 841 权值线段树

休生伤杜景死惊开

Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others)

陆伯言军陷八卦阵之中,分明只是一条直路,却怎的也走不到尽头。阵中尽是石堆,以某一石堆为参考,无论向走还是向右,总是会回到出发的石堆,最后幸得一黄姓老翁带路才得脱出。

陆伯言逃离八卦阵后,来到山顶观察此阵,记从左往右第i堆石堆的高度为 Ai Ai,发现任何两堆较矮的石堆都能和它们之间的一座较高的石堆形成"八卦锁",将其中之人牢牢锁住,无从逃脱。

根据石堆的情况,陆伯言大致计算了“八卦锁”的数量(即  Ai<Aj>Ak,i<j<k Ai<Aj>Ak,i<j<k 的组合数),不禁心中一惊,对孔明惊为天人,遂放弃追击,收兵回吴。

“有劳岳父了。” “为何将其放走?” “...一表人才,何必浪费于此。”

Input

第一行一个整数 n n,表示石堆堆数。

接下来一行, n n个整数,第 i i个数表示从左到右第 i i堆石堆的高度 Ai Ai

1n50000,1Ai32768 1≤n≤50000,1≤Ai≤32768

Output

一个整数,“八阵锁”的数目。

Sample input and output

Sample Input Sample Output
5
1 2 3 4 1
6
 
题目大意:
显然,这道题目要求统计数列中类似“小——大——小”这样形式的数对共有多少。
解题思路:
因此,我们只要求出对于每一个数,左边比它小的树德个数ln[i],以及右边比它小的数的个数rn[i]。将ln[i]和rn[i]相乘加起来就是答案。
维护ln与rn数组,我们将运用 权值线段树
维护一个[0,32768]的线段树(见数据范围),将1~ 32768中的数的出现次数压入线段树。
如果询问比k小的数有多少个,求区间[1,k-1]的和(这也是为什么从0开始建树)。
因为要求ln和rn,建两棵树或初始化(博主为前者)
依次询问每个数,求出ln和rn,并维护答案。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
#define LL long long

const int N=1000010;
const int MAX=1e9+7;
int n,k;
struct Q
{
	char c;
	int a;
}q[N];
int a[N],len;
int ln[N],rn[N];

inline void R(int &v)
{
	v=0;
	char ch=getchar();
	int f=0;
	while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
	while(isdigit(ch)){v=(v<<3)+(v<<1)+ch-'0';ch=getchar();}
	if(f) v=-v;
}
namespace ib {char b[100];}
inline void P(int x)
{
    if(x==0) {putchar(48); return;}
    if(x<0) {putchar('-'); x=-x;}
    char *s=ib::b;
    while(x) *(++s)=x%10, x/=10;
    while(s!=ib::b) putchar((*(s--))+48);
}
struct Segtree
{
	struct trie
	{
		int l,r,len;
		int sum;
		int lz;
	}tree[N<<2];
	
	void updata(int o)//更新 
	{
		tree[o].sum=tree[o<<1].sum+tree[o<<1|1].sum;
	}
	
	void build(int o,int l,int r)//建树 
	{
		tree[o].sum=0;
		tree[o].l=l;
		tree[o].r=r;
		tree[o].len=r-l+1;
		if(l==r) {tree[o].sum=0;return;}
		int mid=(l+r)>>1;
		build(o<<1,l,mid);
		build(o<<1|1,mid+1,r);
		updata(o);
	}
	
	int query(int o,int ql,int qr)//区间和 
	{
		int l=tree[o].l,r=tree[o].r;
		if(ql<=l&&qr>=r) return tree[o].sum;
		int res=0;
		int mid=l+r>>1;
		if(qr<=mid) res+=query(o<<1,ql,qr);
		if(ql>mid) res+=query(o<<1|1,ql,qr);
		if(ql<=mid&&qr>mid)
		{
			res+=query(o<<1,ql,mid)+query(o<<1|1,mid+1,qr);
		}
		return res;
	}
	
	void change(int o,int q,int v)//单点加
	{
		int l=tree[o].l,r=tree[o].r;
		if(l==r) {tree[o].sum+=v;return;}
		int mid=l+r>>1;
		if(q<=mid) change(o<<1,q,v);
		else change(o<<1|1,q,v);
		updata(o);
	}
}A,B;


int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	
	int i,j;
	while(scanf("%d",&n)!=EOF)
	{
		for(i=1;i<=n;i++) scanf("%d",&a[i]);
		A.build(1,0,32768);
		B.build(1,0,32768);
		for(i=1;i<=n;i++)
		{
			ln[i]=A.query(1,0,a[i]-1);
			A.change(1,a[i],1);
		}
		for(i=n;i>=1;i--)
		{
			rn[i]=B.query(1,0,a[i]-1);
			B.change(1,a[i],1);
		}
		LL ans=0;
		for(i=1;i<=n;i++)
		ans+=1ll*ln[i]*rn[i];
		cout<<ans<<endl;
	}
	
	return 0;
}

结语:
*权值线段树
线段树基础题型,权值的思想有点难想到,但知道以后线段树很简单。




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值