「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]);
}
View Code

 

 思路积累

1.考场能减的枝一定要减,万一A了呢.....

2.对于题意性质转化,因为有很多区间的值的贡献是一样的,因为题中答案是$cnt^2$,考虑枚举$\sqrt{n}$的值

posted @ 2019-10-12 21:38  Wwb_star  阅读(145)  评论(0编辑  收藏  举报