CF1603C-Extreme Extension【整除分块,dp】

正题

题目链接:http://codeforces.com/contest/1603/problem/C


题目大意

定义一个序列\(a\)\(f(a)\)为你每次可以将序列中的一个数\(z\)分裂成\(x+y=z\),然后再把\(x,y\)放回原来的位置,然后\(f(a)\)表示把\(a\)变成不降序列的最少操作次数
给出一个长度为\(n\)的序列\(a\),求它所有子区间的\(f\)值的和。

\(1\leq n,a_i\leq 10^5\)


解题思路

显然的每个数字\(a_i\)最终肯定是分解为\(k-1\)\(\lfloor\frac{a_i}{k}\rfloor\)和一个\(\lceil\frac{a_i}{k}\rceil\)
然后我们对于一个序列可以从右往左每个选择能分解的最小的次数来分肯定是最优的。
那么考虑暴力的\(dp\),设\(f_{i,j}\)表示现在第\(i\)个,所有左端点为\(i\)的区间中\(a_i\)分解的最前面那个为\(k\)的方案,然后倒着转移就好了。
考虑到\(j\)肯定是某个\(\lfloor\frac{a_i}{k}\rfloor\),而\(\lfloor\frac{a_i}{k}\rfloor\)最多只有\(2\sqrt a_i\)种取值,所以我们可以只枚举这些取值就好了。

时间复杂度:\(O(n\sqrt a_i)\),略微卡常


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10,P=998244353;
ll T,n,ans,a[N],f[2][N],g[2][N];
signed main()
{
	scanf("%lld",&T);
	while(T--){
		scanf("%lld",&n);
		for(ll i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		ll o=0;
		g[o][a[n]]=1;ans=0;
		for(ll i=n-1;i>=1;i--){
			o^=1;ll m=a[i+1];
			for(ll l=1,r;l<=m;l=r+1){
				r=m/(m/l);
				ll x=m/l,w=(a[i]+x-1)/x,k=a[i]/w;
				f[o][k]+=f[!o][x]+(w-1)*g[!o][x];
				g[o][k]+=g[!o][x];
				ans+=f[!o][x];ans%=P;
				f[!o][x]=g[!o][x]=0;
			}
			g[o][a[i]]++;
		}
		for(ll l=1,r;l<=a[1];l=r+1)
			r=a[1]/(a[1]/l),(ans+=f[o][a[1]/l])%=P,f[o][a[1]/l]=g[o][a[1]/l]=0;
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-10-31 21:56  QuantAsk  阅读(91)  评论(0编辑  收藏  举报