【題解】CF1603C Extreme Extension

CF1603C Extreme Extension

考慮如何計算貢獻,顯然的一般 \(dp\) 套路就是設 \(f[i]\) 爲以 \(i\) 結尾的所有子區間的貢獻。

但是這題我們發現這個結尾要和數字有關。所以先試着寫一下普通的 \(dp:\)

\(f[i][j]\) 表示以 \(i\) 結尾的子區間中最後一個數字是 \(j\) 的價值和。

於是想到倒着做 \(dp\) 這一部分可以讓這一步分解的數字與前文無關。

然後我們發現如果直接計算 extreme value 會很麻煩,直接加也是不好做的

所以我們想到能不能直接算貢獻更簡單,就考慮更改一下 \(f[i][j]\) 表示以 \(i\) 爲左端點且分解完之後開頭是 \(j\) 的方案數。

之所以需要這樣是因爲,考慮我們在一個序列的最前面加入一個數,如果它小於等於上一個數還好說,但是當它大於的時候會發現它的拆分並不是很好轉移。

思路到這裏就停了,實際上還是沒有想明白怎麼貪心做到最優解。

下面分析如何操作。

假定 \(a_i>a_{i+1},\) 那麼我們需要把 \(a_i\) 分解爲 \(b_{1\cdots k}\) 滿足 \(b_1\leq b_2\leq ...\leq b_k,b_k\leq a_{i+1}\)

容易發現,我們需要讓 \(b\) 的長度最小。那麼當 \(b_1\) 最大的時候,即滿足了貪心的要求,也滿足了最短的要求。

這樣我們考慮如何求 \(b.\) 我們發現,由於 \(b_k\leq a_{i+1},\) 所以 \(k=\left\lceil \frac{a_i}{a_{i+1}}\right \rceil\) 是能做到的最優解。

此時也可以做到 \(b_1=\left\lfloor \frac{a_i}{k}\right\rfloor\)

那麼這一個點的貢獻就是 \(k-1\) 次了,分解後的最優解也就對應了 \(b_1.\)

這樣下來就方便算貢獻了。

考慮維護上一層出現的所有數字倒序 \(dp,\) 那麼,對於 \(f[i][j],\) 它對答案的貢獻是多少?

我們發現,設其對應修改次數是 \(x,\)\([i+1][j']\) 轉移而來,那麼它的貢獻就是 \(x\times f[i+1][j']\times i\)

這是因爲,乘以 \(i\) 是和前面所有 \(i\) 個端點匹配都會有這樣一個貢獻,而乘以 \(x\) 是要和前面的方式組合起來。

維護一下決策點就可以做到 \(O(n\sqrt n)\) 了。滾動數組優化一下空間即可。

#include<bits/stdc++.h>
using namespace std;
typedef double db;
#define int long long
const int mod=998244353;
const db eps=1e-14;
inline int Max(int x,int y){return x>y?x:y;}
inline int Min(int x,int y){return x<y?x:y;}
inline db Max(db x,db y){return x-y>eps?x:y;}
inline db Min(db x,db y){return x-y<eps?x:y;}
inline int Add(int x,int y,int M=mod){return (x+y)%M;}
inline int Mul(int x,int y,int M=mod){return 1ll*x*y%M;}
inline int Dec(int x,int y,int M=mod){return (x-y+M)%M;}
inline int Abs(int x){return x<0?-x:x;}
inline int read(){
	int s=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch)){s=s*10+ch-'0';ch=getchar();}
	return s*w;
}
inline void write(int x){
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline int qpow(int x,int y){
	int res=1;
	while(y){
		if(y&1)res=Mul(res,x);
		x=Mul(x,x);y>>=1;
	}
	return res;
}
typedef pair<int,int> pr;
#define fi first
#define se second
#define mk make_pair
#define pb emplace_back
#define poly vector<int>
const int N=2e5+10;
poly v[2];
int T,n,a[N];
int f[2][N];
signed main(){
	T=read();
	while(T--){
		n=read();
		for(int i=1;i<=n;++i)a[i]=read();
		int ans=0,unq;
		for(int i=n;i>=1;--i){
			int pos=i&1;
			v[pos].pb(a[i]);
			f[pos][a[i]]=1;
			unq=a[i];
			for(auto x:v[pos^1]){
				int fm=(a[i]+x-1)/x;
				int num=a[i]/fm;
				ans+=(fm-1)*f[pos^1][x]*i;
				ans%=mod;
				f[pos][num]+=f[pos^1][x];
				f[pos][num]%=mod;
				if(num!=unq){
					unq=num;
					v[pos].pb(unq);
				}
			}
			
			for(auto x:v[pos^1])f[pos^1][x]=0;
			v[pos^1].clear();
		}
		printf("%lld\n",ans);
		for(auto x:v[0])f[0][x]=0,f[1][x]=0;
		for(auto x:v[1])f[1][x]=0,f[0][x]=0;
		v[1].clear();
		v[0].clear();
		
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章