zoukankan      html  css  js  c++  java
  • Increasing Subsequence

    题目大意

    给一个排列,问有多少个极大上升子序列,极大是指这个序列不能是其他上升子序列的子序列。

    题解

    (f_i)表示以(i)结尾的子序列个数,那么转移的话枚举前面比它小的位置,转移的话这两个位置之间不能有这两个值之间的数。
    考虑分治算这个东西,我们分治((l,mid))((mid+1,r))两个区间,计算两个区间之间的贡献,那么我们先去归并这两个序列,在归并的过程中,对于右边序列,我们维护一个位置单调上升的单调栈,左边维护单调下降的单调栈,这样,在右边序列,我们可以利用单调栈找到它前面比它小的最大的元素, 左边的单调栈维护的是合法转移的位置,最后再左边二分出比那个最大的元素大的位置,转移过来即可。

    代码

    #include<bits/stdc++.h>
    #define N 200009
    using namespace std;
    typedef long long ll;
    const int mod=998244353;
    int a[N],n,b[N],c[N],pos[N];
    int st[N],st1[N],dp[N],dp1[N];
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    inline void MOD(int &x){x=x>=mod?x-mod:x;}
    inline void solve(int l,int r){
    	if(l==r)return;
    	int mid=(l+r)>>1;
    	solve(l,mid);
    	for(int i=l;i<=mid;++i){
    		b[i]=a[i];
    	}
    	for(int i=mid+1;i<=r;++i){
    		c[i]=a[i];
    	}
    	sort(b+l,b+mid+1);
    	sort(c+mid+1,c+r+1);
    	int s1=l,s2=mid+1;
    	int top1=0,top=0;
    	while(s1<=mid&&s2<=r){
    		if(b[s1]<c[s2]){
    			while(top1&&pos[st1[top1]]<pos[b[s1]])top1--;
    			st1[++top1]=b[s1];
    			MOD(dp1[top1]=dp1[top1-1]+dp[pos[b[s1]]]);
    			s1++;
    		}
    		else{
    		    while(top&&pos[st[top]]>pos[c[s2]])top--;
    		    int now=0;
    		    if(top)now=st[top];
    		    st[++top]=c[s2];
    		    int x=max(0ll,upper_bound(st1+1,st1+top1+1,now)-st1-1);
    		    MOD(dp[pos[c[s2]]]+=(dp1[top1]-dp1[x]+mod)%mod);
    		    s2++;
    		}
    	}
    	while(s2<=r){
    		while(top&&pos[st[top]]>pos[c[s2]])top--;
    		int now=0;
    		if(top)now=st[top];
    		st[++top]=c[s2];
    		int x=max(0ll,upper_bound(st1+1,st1+top1+1,now)-st1-1);
    		MOD(dp[pos[c[s2]]]+=(dp1[top1]-dp1[x]+mod)%mod);
    		s2++;
    	}
    	
    	solve(mid+1,r);
    }
    int main(){
        int T;
        scanf("%d",&T);
        while(T--){
            n=rd();
            for(int i=1;i<=n;++i)a[i]=rd(),pos[a[i]]=i;
            int mx=1e9;
            for(int i=1;i<=n;++i){
            	if(pos[i]<mx)dp[pos[i]]=1;
            	else dp[pos[i]]=0;
            	mx=min(mx,pos[i]);
            }
            solve(1,n);
            int ans=0;
    		mx=0;
            for(int i=n;i>=1;--i){
            	if(pos[i]>mx)MOD(ans+=dp[pos[i]]);
            	mx=max(mx,pos[i]);
            }
            printf("%lld
    ",ans);
        }
      return 0;
    }
    
  • 相关阅读:
    Java算法练习——整数反转
    Java算法练习—— Z 字形变换
    Java算法练习——最长回文子串
    vs code自动生成html代码
    thinkphp整合后台模板
    composer安装后台模板
    composer(作曲家)安装php-ml
    两个网站
    PHP的开源产品discuz
    onethink中的用户登录session签名
  • 原文地址:https://www.cnblogs.com/ZH-comld/p/15081176.html
Copyright © 2011-2022 走看看