Luogu P1637 三元上升子序列【权值线段树】By cellur925
emmm..不开结构体的线段树真香!
首先我们知道“三元上升子序列”的个数就是对于序列中的每个数,**它左边比他小的数*它右边比他大的数**。但是如何快速求出这两个数?
我们用到权值线段树来维护。一般我们的线段树都是以下标延伸的,但是这里我们用的是权值,一般需要离散化,效果相当于一个桶。
这部分讲解请移步绝世好文
第一次我们从\(1\)~\(n\)循环是为了找它左边的,而找比他小的值是在线段树的\(1\)~\(seq[i]-1\)中找。第二次我们从\(n\)~\(1\)循环是为了找它右边的,而找比他大的值是用在线段树的\(seq[i]+1\)~\(n\)中找实现的。
不用结构体因为方便清空,\(tong\)数组记录线段树的权值,注意开\(4\)倍。
#include<cstdio> #include<algorithm> #include<cstring> #define maxn 100090 using namespace std; typedef long long ll; int n; ll ans,sma[maxn],bigg[maxn]; int seq[maxn],tmp[maxn],tong[maxn<<2]; int ask(int p,int l,int r,int L,int R) { if(L<=l&&r<=R) return tong[p]用线段树求逆序对是同理的,只需要求出每个序列中的位置的左边比它大的数个数就行了。; int mid=(l+r)>>1,qwq=0; if(L<=mid) qwq+=ask(p<<1,l,mid,L,R); if(R>mid) qwq+=ask(p<<1|1,mid+1,r,L,R); return qwq; } void change(int p,int l,int r,int x) { if(l==x&&r==x) { tong[p]++; return ; } int mid=(l+r)>>1; if(x<=mid) change(p<<1,l,mid,x); else if(x>mid) change(p<<1|1,mid+1,r,x); tong[p]=tong[p<<1]+tong[p<<1|1]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&seq[i]),tmp[i]=seq[i]; sort(tmp+1,tmp+1+n); int m=unique(tmp+1,tmp+1+n)-(tmp+1); for(int i=1;i<=n;i++) seq[i]=lower_bound(tmp+1,tmp+1+m,seq[i])-tmp; for(int i=1;i<=n;i++) { if(seq[i]!=1) sma[i]=ask(1,1,n,1,seq[i]-1); change(1,1,n,seq[i]); } memset(tong,0,sizeof(tong)); for(int i=n;i>=1;i--) { if(seq[i]!=n) bigg[i]=ask(1,1,n,seq[i]+1,n); change(1,1,n,seq[i]); } for(int i=1;i<=n;i++) ans+=sma[i]*bigg[i]; printf("%lld\n",ans); return 0; }
#include<cstdio> #include<algorithm> #include<cstring> #define maxn 500090 using namespace std; typedef long long ll; int n; ll ans,lef[maxn]; int tmp[maxn],seq[maxn],tong[maxn<<2]; int ask(int p,int l,int r,int L,int R) { if(L<=l&&r<=R) return tong[p]; int mid=(l+r)>>1,qwq=0; if(L<=mid) qwq+=ask(p<<1,l,mid,L,R); if(R>mid) qwq+=ask(p<<1|1,mid+1,r,L,R); return qwq; } void change(int p,int l,int r,int x) { if(l==x&&r==x) { tong[p]++; return ; } int mid=(l+r)>>1; if(x<=mid) change(p<<1,l,mid,x); else if(x>mid) change(p<<1|1,mid+1,r,x); tong[p]=tong[p<<1]+tong[p<<1|1]; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&seq[i]),tmp[i]=seq[i]; sort(tmp+1,tmp+1+n); int m=unique(tmp+1,tmp+1+n)-(tmp+1); for(int i=1;i<=n;i++) seq[i]=lower_bound(tmp+1,tmp+1+m,seq[i])-tmp; for(int i=1;i<=n;i++) { if(seq[i]!=n) lef[i]=ask(1,1,n,seq[i]+1,n); change(1,1,n,seq[i]); } for(int i=1;i<=n;i++) ans+=lef[i]; printf("%lld\n",ans); return 0; }
- 【bzoj2124】等差子序列 权值线段树维护hash
- 线段树 求一个序列的非递增子序列的权值和的最大值
- bzoj2962 序列操作 线段树
- BZOJ 3065: 带插入区间K小值 替罪羊树套权值线段树 详解
- UVALive - 3938 Ray, Pass methe dishes!"(动态最大连续和子序列,线段树区间合并)
- CSU 1453: 平衡序列 学会线段树后必做
- bzoj2124 等差子序列(hash+线段树)
- P2023 [AHOI2009]维护序列 --线段树
- codevs1688 求逆序对(权值线段树)
- 【CSU 1258 维护序列】+ 线段树
- 【BZOJ】1798 [Ahoi2009]Seq 维护序列seq 线段树
- 【权值线段树】bzoj3224 Tyvj 1728 普通平衡树
- BZOJ1858 序列操作 (线段树)
- BZOJ1798: [Ahoi2009]Seq 维护序列seq[线段树]
- bzoj 维护序列seq(双标记线段树)
- 模板 权值线段树
- 用序列之王splay解决线段树经典问题
- 【BZOJ-1858】序列操作 线段树
- BZOJ 3685 zkw线段树 || 权值线段树
- AHOI 2009 (BZOJ1798)维护序列 seq (线段树好题?)