1 条题解

  • 0
    @ 2025-8-24 23:06:20

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 哈哈人生
    Go Best.

    搬运于2025-08-24 23:06:20,当前版本为作者最后更新于2024-11-23 20:30:25,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题外话

    好久没写题解了,碰巧遇到了这题。
    这题挺有意思的。

    思路

    读完题后,可发现最终答案和不同数字的个数有关,和具体数值无关,所以先把 aia_i 离散化。然后考虑动态规划,设 dpidp_i 表示前 ii 个数分段所得的最小值。先写个最简单的思路,易得转移式为 dpi=minj=0i1dpj+w2dp_i=min_{j=0}^{i-1} dp_j+w^2,其中 dp0=0dp_0=0ww 表示的是 aja_jaia_i 之间不同数字的个数。对于每个 ii,从大到小枚举 jj,顺便用桶记录不同数字的个数,n2n^2 的做法就被我们找到了,加一个小优化可得 4848 分。

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,a[200005],b[200005],dp[200005];
    int lsh[200005],tot=0;
    signed main(){
    	ios::sync_with_stdio(false);
    	cin.tie(0),cout.tie(0);
    	cin>>n;
    	for(int i=1;i<=n;i++)cin>>a[i],lsh[++tot]=a[i];
    	sort(lsh+1,lsh+tot+1);
    	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
    	for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+tot+1,a[i])-lsh;
    	for(int i=1;i<=n;i++){
    		dp[i]=1e18;
    		int w=1;
    		b[a[i]]=i;
    		for(int j=i-1;j>=0;j--){
    			if(w*w>=dp[i])break;//小优化
    			dp[i]=min(dp[i],dp[j]+w*w);
    			if(b[a[j]]!=i)w++,b[a[j]]=i;
    		}
    	}
    	cout<<dp[n];
    	return 0;
    }
    

    那这个思路是不是陷入瓶颈了呢?再重新想一下哪些转移位置是真的有用的。设 dpidp_i 是从某个位置 jj 上转移来的,而 jj 若满足 aj1a_{j-1} 是在 aja_jaia_i 中出现过的,那么其显然不如从位置 j1j-1 上转移,因为多一个已出现的 aj1a_{j-1} 无任何增加的花费,所以此时位置 jj 无用,也就是说最优的 dpidp_i 一定不是从 jj 转移过来的。把这个规律普遍化一下,就会发现对于 dpidp_i 来说,一个位置 jj 真正有用就需要满足不存在 j+1kij+1\le k\le i,使得 ak=aja_k=a_j。我们可以用一个桶来存所有数字最后出现的位置,再用 set 来维护之间的先后顺序,每次转移都从 set 中所存的位置一一尝试选取最优解。但这样乍一看,set 的大小最多也是 ii,似乎还是 n2n^2 的时间复杂度。但别忘了我们还有一个小优化:if(w*w>=dp[i])break;。之前这个优化的作用不明显是因为每个位置可能数字出现过也有可能没出现过,构造特定数据还是能使循环执行接近 ii 次。但这回我们每访问 set 中的一个位置,ww 必定加一,因为我们存的是每个数字最后一次出现的位置。所以这个代码就使我们的时间复杂度变为 O(nn)O(n\sqrt n)dpidp_i 的最大取值也不过是 ii,而且显然会小很多),并且大概率跑不满。

    正解代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    int n,a[200005],b[200005],dp[200005];
    int lsh[200005],tot=0; 
    set<int> s;
    typedef int type;
    inline type read(){
    	type x=0,f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*f;
    }
    inline void write(type x){
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x / 10);
    	putchar(x%10+'0');
    }
    signed main(){
    	n=read();
    	for(int i=1;i<=n;i++)a[i]=read(),lsh[++tot]=a[i];
    	sort(lsh+1,lsh+tot+1);
    	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
    	for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+tot+1,a[i])-lsh;
    	for(int i=1;i<=n;i++){
    		dp[i]=1e18;
    		if(b[a[i]])s.erase(s.find(-b[a[i]]));
    		int w=1;
    		for(auto it=s.begin();it!=s.end();it++){
    			int j=-*it;
    			dp[i]=min(dp[i],dp[j]+w*w);
    			w++;
    			if(w*w>=dp[i])break;
    		}
    		dp[i]=min(dp[i],w*w);
    		b[a[i]]=i,s.insert(-i);//存-i是在使set从大往小排列位置
    	}
    	write(dp[n]);
    	return 0;
    }
    

    后记

    点个赞吧。

    • 1

    信息

    ID
    9291
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者