休生伤杜景死惊开
Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others)
陆伯言军陷八卦阵之中,分明只是一条直路,却怎的也走不到尽头。阵中尽是石堆,以某一石堆为参考,无论向走还是向右,总是会回到出发的石堆,最后幸得一黄姓老翁带路才得脱出。
陆伯言逃离八卦阵后,来到山顶观察此阵,记从左往右第i堆石堆的高度为 Ai ,发现任何两堆较矮的石堆都能和它们之间的一座较高的石堆形成"八卦锁",将其中之人牢牢锁住,无从逃脱。
根据石堆的情况,陆伯言大致计算了“八卦锁”的数量(即 Ai<Aj>Ak,i<j<k 的组合数),不禁心中一惊,对孔明惊为天人,遂放弃追击,收兵回吴。
“有劳岳父了。” “为何将其放走?” “...一表人才,何必浪费于此。”
Input
第一行一个整数 n ,表示石堆堆数。
接下来一行, n 个整数,第 i 个数表示从左到右第 i 堆石堆的高度 Ai 。
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;
}
结语:
*权值线段树
线段树基础题型,权值的思想有点难想到,但知道以后线段树很简单。