UOJ497 新年的复读机

题意

给定 n 个数 ai,你每次操作可以选择相邻两个数 ai,ai+1,代价自加 ai+ai+1,然后把这两个数删掉,并在原处添加一个 gcd(ai,ai+1),直到只剩一个数,你要最小化此时代价。

n2×105,ai1012

分析

考虑暴力区间 DP:设 fl,r 表示消除 [l,r] 的最小代价。复杂度 O(n3)

考虑一个重要性质:每次只往外扩展一个数不劣

感性证明:考虑四个数 a1,a2,a3,a4,我们要证的相当于 (((12)3)4)(1(2(34))) 之中必定有一个比 ((12)(34)) 不劣。

首先将四个数同除它们的 gcd 没有影响。然后,令 x=gcd(a1,a2,a3),y=gcd(a2,a3,a4),不妨设 gcd(a1,a2)=k1x,gcd(a3,a4)=k4y。反证,若 (((12)3)4)(1(2(34))) 都比 ((12)(34)) 劣,推推式子抵消一下发现 k4y<x,k1x<y,两式相加得 k1x+k4y<x+y,而 k1,k41,矛盾。

由此做到 O(n2) 是容易的。


再考虑另一个重要性质:对于一个 lgcdi=lrai 本质不同的 r 数量只有 O(logV)

证明:考虑 r 扩大时如果 gcd 变化,那么一定变化成原来 gcd 的一半。

如果此时把这些分界点拿出来,然后枚举初始点,直接 DP 状态数是 O(nlog2V) 的。考虑继续优化。


我们可以将 i 为左端点、右端点时把这些有用的区间拿出来,那么一共只有 O(nlogV) 个区间。

考虑对这些区间做 DP,首先将这些区间按照长度排序,桶排鸡排都行。

考虑能转移到 fi 的状态,手玩一下实际上就是左端点的上一个区间和右端点的上一个区间,转移也比较好推。具体见 code。

最后介绍一下怎么求合法区间。

以固定右端点为例,考虑求出 r1 时的分界点情况以及 gcd 值,然后加入 ai,将这些分界点的 gcd 值与 ai 取 gcd,然后再加入一个单独的 ai,然后对这个玩意去重,为了方便,要保留左端点是 1 的区间。这样做的复杂度是 O(nlog2V) 的,据说可证成 O(nlogV),但我不会。实测这东西跑 1e6 跑的飞快,一般不是瓶颈,无需担心。

const int maxn=2e5+5,maxm=2e7+5,inf=0x3f3f3f3f;
const long long llinf=0x3f3f3f3f3f3f3f3f;
int n,a[maxn],s[maxn];
inline int gcd(int x,int y){
	return !y?x:gcd(y,x%y);
}
struct seg{
	int l,r,g;
}b[maxm];
int cnt;
pii q[maxn],qq[maxn];
int qcnt;
vector<seg>vec[maxn];
int lstl[maxn],lstr[maxn],f[maxm];
inline void solve_the_problem(){
	n=rd();
	rep(i,1,n)a[i]=rd(),s[i]=s[i-1]+a[i];
	rep(i,1,n){
		rep(j,1,qcnt)q[j].se=gcd(q[j].se,a[i]);
		q[++qcnt]=mp(i,a[i]);
		int pcnt=0;
		per(j,qcnt,1){
			if(q[j].fi==1||q[j].fi==i||q[j+1].se!=q[j].se){
				qq[++pcnt]=q[j];
			}
		}
		qcnt=pcnt;
		rep(j,1,qcnt)q[j]=qq[qcnt-j+1];
		rep(j,1,qcnt){
			b[++cnt]=(seg){q[j].fi,i,q[j].se};
		}
	}
	qcnt=0;
	per(i,n,1){
		rep(j,1,qcnt)q[j].se=gcd(q[j].se,a[i]);
		q[++qcnt]=mp(i,a[i]);
		int pcnt=0;
		per(j,qcnt,1){
			if(q[j].fi==n||q[j].fi==i||q[j+1].se!=q[j].se){
				qq[++pcnt]=q[j];
			}
		}
		qcnt=pcnt;
		rep(j,1,qcnt)q[j]=qq[qcnt-j+1];
		rep(j,1,qcnt){
			b[++cnt]=(seg){i,q[j].fi,q[j].se};
		}
	}
	rep(i,1,cnt){
		vec[b[i].r-b[i].l+1].emplace_back((seg){b[i].l,b[i].r,b[i].g});
	}
	cnt=0;
	rep(i,1,n){
		for(auto j:vec[i])b[++cnt]=j;
		vec[i].clear(),vec[i].shrink_to_fit();
	}
	rep(i,1,cnt){
		const int l=b[i].l,r=b[i].r,L=lstl[l],R=lstr[r];
		if(l==r){
			lstl[l]=lstr[r]=i,f[i]=0;
			continue;
		}
		f[i]=min(f[L]+b[L].g*(r-b[L].r)+s[r]-s[b[L].r],f[R]+b[R].g*(b[R].l-l)+s[b[R].l-1]-s[l-1]);
		lstl[l]=lstr[r]=i;
	}
	write(f[cnt]);
}

作者:dcytrl

出处:https://www.cnblogs.com/dcytrl/p/18727807

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   dcytrl  阅读(10)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up light_mode palette
选择主题
点击右上角即可分享
微信分享提示