zoukankan      html  css  js  c++  java
  • CF1474F 1 2 3 4 ...

    一、题目

    点此看题

    二、解法

    理解本题给出序列的方式很重要,我们把它放在坐标轴上,那么我们枚举一个转折点,然后找它后面最高的转折点,求最大差值就可以求出最长上升子序列长度,关键在于求出子序列个数。

    不难发现不同的最低点和最高点组合范围一定不交,如果相交可以通过调整获得更优解,所以每一段可以单独处理。

    坐标的范围达到了 (1e9),肯定往矩阵加速上想,正常递推的顺序是按照 (x) 轴从左往右,可以记录以这个点结尾的方案数,但是硬要这么转移你需要记录每个 (y) 结尾的方案数,那这做锤子。

    问题在于无法记录 (y),那么我们(y) 的顺序递推不就不需要记录它了吗?所以设计状态 (f_{v,i}) 表示第 (i) 段以 (y=v) 结尾有多少种方案,我们先把所有转折点的 (y) 离散化,然后对于一小段它们的转移方式是相同的,所以可以矩阵加速第一维,时间复杂度 (O(n^4log n))

    注意每一段要保证左闭右闭,所以端点一定要特别注意,还有如果答案长度为 (1) 需要特判。

    三、总结

    走到绝境的时候应该思考,(dp) 状态、转移等是否合理,不要受困于定式思维。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define int long long
    const int M = 55;
    const int MOD = 998244353;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,s,ans,res,a[M],l[M],r[M],d[M*M];
    struct mat
    {
    	int a[M][M];
    	void clear() {memset(a,0,sizeof a);}
    	mat() {clear();}
    	mat operator * (const mat &b) const
    	{
    		mat r;
    		for(int i=0;i<=m;i++)
    			for(int j=0;j<=m;j++)
    				for(int k=0;k<=m;k++)
    					r.a[i][k]=(r.a[i][k]+a[i][j]*b.a[j][k])%MOD;
    		return r;
    	}
    	void print()
    	{
    		for(int i=0;i<=m;i++,puts(""))
    			for(int j=0;j<=m;j++)
    				printf("%d ",a[i][j]);
    	}
    }A,B;
    mat qkpow(mat a,int b)
    {
    	mat r;
    	for(int i=0;i<=m;i++) r.a[i][i]=1;
    	while(b>0)
    	{
    		if(b&1) r=r*a;
    		a=a*a;
    		b>>=1;
    	}
    	return r;
    }
    void solve(int L,int R)
    {
    	m=k=0;
    	for(int i=L,t=s;i<=R;t+=a[i],i++)
    	{
    		l[++m]=(a[i]>0)?t+1:t-1;
    		r[m]=t+a[i];
    		if(m==1) l[1]=s;
    		d[++k]=l[m],d[++k]=l[m]-1,d[++k]=l[m]+1;
    		d[++k]=r[m],d[++k]=r[m]-1,d[++k]=r[m]+1;
    	}
    	sort(d+1,d+1+k);
    	A.clear();A.a[0][0]=1;
    	k=unique(d+1,d+1+k)-d-1;
    	for(int i=1,ls=s-1;i<=k;i++)
    	{
    		B.clear();
    		if(d[i]<=ls || d[i]>s+ans) continue;
    		for(int j=1;j<=m;j++)
    			if(min(l[j],r[j])<=ls+1 && max(l[j],r[j])>=d[i])
    			{
    				for(int k=0;k<=j;k++)
    					B.a[k][j]=1;
    				if(l[j]>=r[j]) B.a[j][j]=0;
    			}
    		A=A*qkpow(B,d[i]-ls);ls=d[i];
    	}
    	for(int i=0;i<=m;i++)
    		res=(res+A.a[0][i])%MOD;
    }
    signed main()
    {
    	n=read();read();
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		if(!a[i]) {i--;n--;continue;}
    	}
    	for(int i=1;i<=n;s+=a[i],i++)
    		for(int j=i,t=s;j<=n;j++)
    		{
    			t+=a[j];
    			ans=max(ans,t-s);
    		}
    	if(!ans)
    	{
    		printf("1 %lld
    ",(-s+1)%MOD);
    		return 0;
    	}
    	s=0;
    	for(int i=1;i<=n;s+=a[i],i++)
    	{
    		int r=-1;
    		for(int j=i,t=s;j<=n;j++)
    		{
    			t+=a[j];
    			if(t-s==ans) r=j;
    		}
    		if(r>0) solve(i,r),i=r;
    	}
    	printf("%lld %lld
    ",ans+1,res);
    }
    
  • 相关阅读:
    tensorflow 学习
    join-semi and join-anti
    深入拆解Tomcat_Jetty 笔记
    Set化
    DDD实战-笔记
    高并发系统设计-笔记
    技术管理
    性能调优-笔记
    程序员是如何思考的-笔记
    LeetCode
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15113113.html
Copyright © 2011-2022 走看看