1. 程式人生 > 其它 >小程式之——五級聯動日期選擇器

小程式之——五級聯動日期選擇器

一、題目

點此看題

二、解法

理解本題給出序列的方式很重要,我們把它放在座標軸上,那麼我們列舉一個轉折點,然後找它後面最高的轉折點,求最大差值就可以求出最長上升子序列長度,關鍵在於求出子序列個數。

不難發現不同的最低點和最高點組合範圍一定不交,如果相交可以通過調整獲得更優解,所以每一段可以單獨處理。

座標的範圍達到了 \(1e9\),肯定往矩陣加速上想,正常遞推的順序是按照 \(x\) 軸從左往右,可以記錄以這個點結尾的方案數,但是硬要這麼轉移你需要記錄每個 \(y\) 結尾的方案數,那這做錘子。

問題在於無法記錄 \(y\),那麼我們\(y\) 的順序遞推不就不需要記錄它了嗎?所以設計狀態 \(f_{v,i}\)

表示第 \(i\) 段以 \(y=v\) 結尾有多少種方案,我們先把所有轉折點的 \(y\) 離散化,然後對於一小段它們的轉移方式是相同的,所以可以矩陣加速第一維,時間複雜度 \(O(n^4\log 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\n",(-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\n",ans+1,res);
}