「10.12」木板(数学)·打扫卫生(神仙DP)
A. 木板
一个很简单的数学题,简单推一下就好,路丽姐姐教你学数学。
将式子化出我们发现只需求出$i\times i/n$的个数
那么我们将$n$质因数分解,可知因子个数
为了整除$n$,令$i==\sqrt{n\times k} $,我们需要让$k$含有$n$中奇数个数的因子
然后同时还可以有其他的平方因子,直接爆求即可。
思路积累:
1.对于求$i\times i/n$可以从$n$的因子上下手
B. 打扫卫生
一个$DP$的大神题,考场被各种剪枝cao过
$ \%\%\%\%\%kx,Duanyue $线段树$AC$
$n^2$DP显然不说了
正解:
首先记清几个数组的定义:
$pre_{i}$表示当前遍历情况下权值为$i$的点的最近出现的位置
$pos_{j}$表示当前从$pos_{j}+1$到$i$之间不同的数字小于等于$j$的位置
$cnt_{j}$表示当前从$pos_{j}+1$到$i$之间不同的数字个数
然后分析题意,因为最坏情况下,我们分$n$段权值为$n$
所以对于每段的数字个数我们不能取过$\sqrt{n}$;
那么我们考虑转移:
但我们添加一个新的数时
假如他的$pre_{a_{i}}$大于当前枚举的$pos_{j}$,那么$a_{i}$已经不是第一次出现了
所以对$cnt_{j}$没有贡献
但是假如小于等于的话....显然此时$cnt_{j}++$可能大于$j$,所以此时$pos_{j}$要移动
那么在移动时我们要让$cnt_{j}--$,所以我们应该找到第一个$pre_{a_{pos_{j}}}$等于$pos_{j}$的
因为这样意味着我们将$pos_{j}$移到此处时,正好减去了一个元素,使元素不在$pos_{j}+1$到$i$出现
复杂度证明:
每个指针即每个$j$有$\sqrt{n}$个且最多移动$n$次。

#include<bits/stdc++.h> #define MAXN 41000 using namespace std; int read(){ int x=0;char c=getchar();int ff=1; while(c<'0'||c>'9'){if(c=='-')ff=-1;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*ff; } int pre[MAXN],cnt[MAXN],pos[MAXN],f[MAXN];int n,m; int a[MAXN]; signed main(){ memset(f,0x3f3f3f,sizeof(f)); f[0]=0; n=read();m=read(); for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=n;++i){ for(int j=1;j<=sqrt(n);++j){ //printf("pre[%d]=%d\n",a[i],pre[a[i]]); if(pre[a[i]]<=pos[j]){ cnt[j]++; if(cnt[j]>j){ pos[j]++; while(pre[a[pos[j]]]>pos[j]){ pos[j]++; } cnt[j]=j; } } f[i]=min(f[pos[j]]+cnt[j]*cnt[j],f[i]); //printf("f[%d]=%d pos[%d]=%d\n",i,f[i],j,pos[j]); } pre[a[i]]=i; } printf("%d\n",f[n]); }
思路积累
1.考场能减的枝一定要减,万一A了呢.....
2.对于题意性质转化,因为有很多区间的值的贡献是一样的,因为题中答案是$cnt^2$,考虑枚举$\sqrt{n}$的值
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步