楼兰图腾 题解

楼兰图腾

【题目描述】

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

Input Data

第一行一个数 n
第二行是 n 个数,分别代表 y1,y2,...,yn

Output Data

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

Input

5
1 5 3 2 4

Output

3 4

Data Limit

10%的数据 n≤600
40%的数据 n≤5000
100%的数据 n≤200000

思路

首先,10分思路就是直接暴力,直接枚举i,j,k。这里就不再说明了。

再进一步,对于40%的数据,可以n^2的暴力,使用结构体,按照数值排序一遍,然后再枚举j点,最后从1~j-1枚举i,寻找a[i].id>a[j].id sum1++;寻找a[i].id<a[j].id sum2++,每回合,将ans加上sum之积就好了。

有人会问?k呢?

就是sum2啊

i呢?

就是sum1啊

再运用组合,所以ans+=sum1*sum2。

100分思路:

考虑优化掉i的循环。将其转换成logn级别的算法。那么可以想到使用stl的set。但是本人太弱,打不来set。所以就直接上平衡树的模板,寻找一下该点在treap中的排名。

代码如下:

#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stack>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cstring>
#include<cmath>
#define ll long long 
#define eps 1e-4
using namespace std;
//priority_queue<int,vector<int>,greater<int> > q1;
//priority_queue<int> q2;
//std::set<int> s;
//list<int> l;
//map<int> mp;
//queue<int> q;
inline ll read(){
    ll ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
inline void write(ll zx){
    if(zx<0){zx=-zx;putchar('-');}
    if(zx<10) putchar(zx+'0');
    else{
        write(zx/10);
        putchar(zx%10+'0');
    }
}

struct data{
    ll lc,rc,key,pri,cnt,sze;
    #define lc(x) t[x].lc
    #define rc(x) t[x].rc
    #define v(x) t[x].key
    #define p(x) t[x].pri
    #define c(x) t[x].cnt
    #define s(x) t[x].sze
}t[500010];
ll n,rt;
ll f[500010],ans1,ans2,pool;
struct node{
    ll num,id;
}a[500010];
inline void upt(const ll &k){s(k)=s(lc(k))+s(rc(k))+c(k);}
inline void Zig(ll &k){
    ll y=lc(k);
    lc(k)=rc(y);
    rc(y)=k;
    s(y)=s(k);
    upt(k);
    k=y;
}
inline void Zag(ll &k){
    ll y=rc(k);
    rc(k)=lc(y);
    lc(y)=k;
    s(y)=s(k);
    upt(k);
    k=y;
}
inline void Insert(ll &k,const ll &key){
    if(!k){
        k=++pool;v(k)=key;p(k)=rand();
        c(k)=s(k)=1;lc(k)=rc(k)=0;
        return ;
    }
    else ++s(k);
    if(v(k)==key) ++c(k);
    else if(key<v(k)){
        Insert(lc(k),key);
        if(p(lc(k))<p(k)) Zig(k);
    }else{
        Insert(rc(k),key);
        if(p(rc(k))<p(k)) Zag(k);
    }
    return ;
}
inline ll QueryRank(const ll &key){
    ll x=rt,res=0;
    while(x){
        if(key==v(x)) return res+s(lc(x))+1;
        if(key<v(x)) x=lc(x);
        else res+=s(lc(x))+c(x),x=rc(x);
    }
}
inline void Delete(ll &k,const ll &key){
    if(v(k)==key){
        if(c(k)>1) --c(k),--s(k);
        else if(!lc(k)||(!rc(k))) k=lc(k)+rc(k);
        else if(p(lc(k))<p(rc(k))) Zig(k),Delete(k,key);
        else Zag(k),Delete(k,key);
        return ;
    }
    --s(k);
    if(key<v(k)) Delete(lc(k),key);
    else Delete(rc(k),key);
    return ;
}//平衡树模板
bool cmp(node qx,node qy){
    return qx.num<qy.num;
}
bool cmp2(node qx,node qy){
    return qx.num>qy.num;
}
int main(){
    freopen("E.in","r",stdin);
    freopen("E.out","w",stdout);
    n=read();
    for(ll i=1;i<=n;i++) a[i].id=i,a[i].num=read();
    sort(a+1,a+n+1,cmp);
    for(ll i=1;i<=n;i++){
//        Insert(a[i].id);
    /*    s.insert(a[i].id);
        s.upper_bound(s.begin(),s.end(),a[i].id);
*///一开始想使用set的,忘记了怎么打
Insert(rt,a[i].id);
ll f1=QueryRank(a[i].id);f1--;
ll f2=i-f1-1;
/*
ll f1=0,f2=0;
for(ll j=1;j<i;j++){
    if(a[j].id<a[i].id) f1++;
    if(a[j].id>a[i].id) f2++;
}
*///暴力写法
ans2+=f1*f2;
    }
    for(ll i=1;i<=n;i++){
        Delete(rt,a[i].id);
    }//记得清除
    sort(a+1,a+n+1,cmp2);
    for(ll i=1;i<=n;i++){
//        Insert(a[i].id);
/*
        s.insert(a[i].id);
        s.upper_bound(s.begin(),s.end(),a[i].id);
*/
Insert(rt,a[i].id);
ll f1=QueryRank(a[i].id);f1--;
ll f2=i-f1-1;
ans1+=f1*f2;
    }
    write(ans1);putchar(' ');
    write(ans2);putchar('\n');//输出
    return 0;
}

 

转载于:https://www.cnblogs.com/yzx1798106406/p/9747153.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值