洛谷 P1637 三元上升子序列(树状数组)

传送门
日常来水一篇(滑稽)
这题本来觉得像逆序对那样插入,查询,然后ans+=num*(num-1),然后两个样例都没过,才发现这么是错的,因为这样前两个的顺序就无法保证了。
然后就想正解。
然后就想到一个类似dp的做法:

f(i,j)ij
然后就可以这样dp:
f(i,j)=i1k=1[ak<ai]f(k,j1)

然后这就是 O(n2)

发现可以用树状数组优化
因为dp的过程实际就是查找所有小于它的数的长度减一的上升子序列数量
然后就可以离散化之后插入,查询
时间复杂度: O(nlogn)

代码:

#include
#include
#include
#include
#include
#include
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,size;
ll a[30001];
ll b[30001];
int t[30001];
inline int lowbit(int x){
    return x&-x;
}
inline void update(int x){
    while(x<=size){
        t[x]++;
        x+=lowbit(x);
    }
}
inline int query(int x){
    int ans=0;
    while(x){
        ans+=t[x];
        x-=lowbit(x);
    }
    return ans;
}
ll t2[30001];
inline void update2(int x,int v){
    while(x<=size){
        t2[x]+=v;
        x+=lowbit(x);
    }
}
inline int query2(int x){
    int ans=0;
    while(x){
        ans+=t2[x];
        x-=lowbit(x);
    }
    return ans;
}
inline int Hash(ll x){
    return lower_bound(b+1,b+size+1,x)-b;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        b[i]=a[i];
    }
    sort(b+1,b+n+1);
    size=unique(b+1,b+n+1)-b-1;
    ll ans=0;
    for(int i=1;i<=n;i++){
        int rk=Hash(a[i]);
        update(rk);
        int num=query(rk-1);
        update2(rk,num);
        ans+=query2(rk-1);
    }
    printf("%lld",ans);
    return 0;
}

你可能感兴趣的:(树状数组又短又好写)