• 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
    ",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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    database join
    图像超分辨率重建
    信号处理
    将博客搬至CSDN
    Openstack
    nginx的优化
    CentOS系统的优化
    zabbix服务端客户端部署
    MySQL优化必须调整的10项配置
    TCP三次握手
  • 原文地址:https://www.cnblogs.com/nopartyfoucaodong/p/9903039.html
Copyright © 2020-2023  润新知