1. 程式人生 > 實用技巧 >Codeforces 1474F. 1 2 3 4 ... 題解

Codeforces 1474F. 1 2 3 4 ... 題解

題目大意:給定一個序列,求其中最長嚴格上升子序列長度及其個數。

序列按如下方式給出:給定 \(n(1\leq n\leq 50)\) 和序列中的第一個數 \(x(-10^9\leq x\leq 10^9)\),接下來 \(n\) 個數 \(d_i(-10^9\leq d_i\leq 10^9)\),若 \(d_i>0\),則重複 \(d_i\) 次,在序列末尾加上當前序列最後一個數 \(+1\) 的值,若 \(d_i<0\),重複 \(-d_i\) 次,在序列末尾加上當前序列最後一個數 \(-1\) 的值。


題解:首先 \(x\) 沒有任何用處,證明顯然。

首先求最長上升子序列長度非常容易,直接 \(O(n^2)\)

暴力就可以了,具體來說,因為值是連續的,所以我們只需要知道一個最長上升子序列的最小值和最大值就可以知道它的長度了,那麼這個地方直接列舉起點(容易發現一定在 \(d_i\) 做完的位置上),然後計算一下之後的最大值就可以了。

接下來的主要問題是如何求出最長上升子序列的方案數,容易發現如果構成最長上升子序列的最大值和最小值不唯一,那麼這些最大值和最小值所在的區間一定不交,所以可以直接拆開來算,所以我們只需要考慮只有一種最小值和最大值的情況。

假設序列開頭是最小值(這容易做到),那麼我們就可以考慮 DP 了,設 \(f_{v,i}\) 表示當前的值是 \(v\),當前所在的段是 \(i\)(段就是一整段上升或下降)的方案數,這個轉移十分顯然,並且容易發現在很大的一段區間內轉移都是相同的,可以採用矩陣快速冪來優化。

不過還是需要特判一下 \(d_i\) 全部都是負數的情況。

時間複雜度:\(O(n^3\log A)\)(其中 \(A\) 是值域)。

程式碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Maxn=50;
const int Mod=998244353;
int n;
int a[Maxn+5],a_len;
int m;
struct Matrix{
	int a[Maxn+5][Maxn+5];
	void init_zero(){
		for(int i=0;i<=m;i++){
			for(int j=0;j<=m;j++){
				a[i][j]=0;
			}
		}
	}
	void init(){
		for(int i=0;i<=m;i++){
			for(int j=0;j<=m;j++){
				a[i][j]=(i==j);
			}
		}
	}
	friend Matrix operator *(Matrix a,Matrix b){
		Matrix ans;
		ans.init_zero();
		for(int i=0;i<=m;i++){
			for(int k=0;k<=m;k++){
				for(int j=0;j<=m;j++){
					ans.a[i][j]=(ans.a[i][j]+1ll*a.a[i][k]*b.a[k][j])%Mod;
				}
			}
		}
		return ans;
	}
};
Matrix quick_power(Matrix a,int b){
	Matrix ans;
	ans.init();
	while(b){
		if(b&1){
			ans=ans*a;
		}
		b>>=1;
		a=a*a;
	}
	return ans;
}
struct Vector{
	int a[Maxn+5];
	void init_zero(){
		for(int i=0;i<=m;i++){
			a[i]=0;
		}
	}
	friend Vector operator *(Vector a,Matrix b){
		Vector ans;
		ans.init_zero();
		for(int i=0;i<=m;i++){
			for(int j=0;j<=m;j++){
				ans.a[j]=(ans.a[j]+1ll*a.a[i]*b.a[i][j])%Mod;
			}
		}
		return ans;
	}
};
struct Segment{
	ll l,r;
}seg[Maxn+5];
int seg_len;
ll d[Maxn*6+5];
int d_len;
int solve(int l,int r){
	m=r-l+1;
	seg_len=0;
	ll x=0;
	for(int i=l;i<=r;i++){
		seg_len++;
		seg[seg_len].l=(x+(a[i]<0?-1:1));
		seg[seg_len].r=x+a[i];
		x+=a[i];
	}
	seg[1].l=0;
	d_len=0;
	for(int i=1;i<=seg_len;i++){
		d[++d_len]=seg[i].l-1;
		d[++d_len]=seg[i].l;
		d[++d_len]=seg[i].l+1;
		d[++d_len]=seg[i].r-1;
		d[++d_len]=seg[i].r;
		d[++d_len]=seg[i].r+1;
	}
	sort(d+1,d+1+d_len);
	d_len=unique(d+1,d+1+d_len)-d-1;
	d_len--;
	Vector ans;
	ans.init_zero();
	ans.a[0]=1;
	ll lst=-1;
	for(int i=1;i<=d_len;i++){
		Matrix tmp;
		tmp.init_zero();
		for(int j=1;j<=seg_len;j++){
			if(min(seg[j].l,seg[j].r)<=lst+1&&max(seg[j].l,seg[j].r)>=d[i]){
				if(seg[j].l<seg[j].r){
					for(int k=0;k<=j;k++){
						tmp.a[k][j]=1;
					}
				}
				else{
					for(int k=0;k<j;k++){
						tmp.a[k][j]=1;
					}
				}
			}
		}
		tmp=quick_power(tmp,d[i]-lst);
		ans=ans*tmp;
		lst=d[i];
	}
	int sum=0;
	for(int i=0;i<=m;i++){
		sum=(sum+ans.a[i])%Mod;
	}
	return sum;
}
int main(){
	scanf("%d%*d",&n);
	for(int i=1;i<=n;i++){
		int x;
		scanf("%d",&x);
		if(x!=0){
			a[++a_len]=x;
		}
	}
	n=a_len;
	ll ans=0;
	ll sum=0;
	for(int i=1;i<=n;i++){
		ll tmp=sum;
		for(int j=i;j<=n;j++){
			tmp+=a[j];
			ans=max(ans,tmp-sum);
		}
		sum+=a[i];
	}
	if(ans==0){
		int val=1;
		for(int i=1;i<=n;i++){
			val=(val-a[i])%Mod;
		}
		printf("%lld %d\n",ans+1,val);
		return 0;
	}
	sum=0;
	int val=0;
	for(int i=1;i<=n;i++){
		ll tmp=sum;
		int right=-1;
		for(int j=i;j<=n;j++){
			tmp+=a[j];
			if(tmp-sum==ans){
				right=j;
			}
		}
		sum+=a[i];
		if(right!=-1){
			val=(val+solve(i,right))%Mod;
			i=right;
		}
	}
	printf("%lld %d\n",ans+1,val);
	return 0;
}