Luogu P1637 三元上升子序列【權值線段樹】By cellur925
阿新 • • 發佈:2018-11-04
emmm..不開結構體的線段樹真香!
首先我們知道“三元上升子序列”的個數就是對於序列中的每個數,**它左邊比他小的數*它右邊比他大的數**。但是如何快速求出這兩個數?
我們用到權值線段樹來維護。一般我們的線段樹都是以下標延伸的,但是這裡我們用的是權值,一般需要離散化,效果相當於一個桶。
這部分講解請移步絕世好文
第一次我們從\(1\)~\(n\)迴圈是為了找它左邊的,而找比他小的值是線上段樹的\(1\)~\(seq[i]-1\)中找。第二次我們從\(n\)~\(1\)迴圈是為了找它右邊的,而找比他大的值是用線上段樹的\(seq[i]+1\)~\(n\)中找實現的。
不用結構體因為方便清空,\(tong\)
#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; }