T1 木板
求$[\sqrt{n},n)$间有多少个数的平方是n的倍数
通过打表可以发现(我没带脑子我看不出来),符合条件的数构成一个等差数列,公差为首项
而首项就是将n质因数分解后每个质因数出现次数除二,向上取整,这个数一定是大于$\sqrt{n}$的最小的符合条件的数
$\sqrt{n}$将$n$分解质因数后求出首项,$(n-1)/$首项就是小于$n$里有几个,即答案
积累:$papa$大神教我,打表之前先猜规律,用小点看看猜的对不对,然后再用大点验证,想不出来的数学题就打打表,找找规律

#include#include #include #define ll long long using namespace std; ll n,ans,z[100100],cnt,num[100100]; ll read() { ll aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc<='9'&&cc>='0'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } void devide(ll x) { ll xx=sqrt(x); for(ll i=2;i<=xx;i++){ if(x%i==0){ z[++cnt]=i;num[cnt]=0; while(x%i==0) x/=i,num[cnt]++; } } if(x>1) z[++cnt]=x,num[cnt]=1; } ll quick(ll x,ll p) { ll as=1; while(p){ if(p&1) as=as*x; x=x*x; p>>=1; } return as; } int main() { while(1){ n=read();ans=1;cnt=0; if(!n) break; devide(n); for(ll i=1;i<=cnt;i++) ans*=quick(z[i],(num[i]+1)/2); printf("%lld\n",(n-1)/ans*8); } return 0; }
T2 打扫卫生
首先肯定是$dp$
考场写的是$n^2$的,每次用桶把$a[j]$的贡献减去,更新答案,最后再循环一遍都加回来,$T60$
但我们发现如果不同的数的个数大于了$\sqrt{n}$,那么他一定不优(大于$\sqrt{n}$,那还不如一段就一个,这样就只有$n$)
所以我们可以在往回加的时候特判,如果$cnt$大于$\sqrt{n}$,那就记录下这个位置,然后$break$,下次循环就只从$pos$循环就行了,加上这个减枝可以到$T80$
正解:$cnt$一定小于$\sqrt{n}$,那么我们枚举i前不同的数的个数,复杂度就降到了$O(n\sqrt{n})$
用$pre[a[i]]$记录$a[i]$上次出现的位置,$b[j]$表示从$b[j]+1~i$一共有$j$个不同的数,$c[j]$表示从$b[j]+1~i$有多少个不同的数
$i++$后,如果$pre[a[i]]<=j$,说明$a[i]$在$b[j]+1~i-1$这一段没有出现过,所以$b[j]+1~i$这一段中不同数的个数就变成了$c[j]+1$,更新$c[j]$
如果$c[j]>j$,我们为了维护$c[j]==j$,就需要调整$b[j]$的位置,使$b[j]$合法
设$pos=b[j]+1$(原位置),如果$pre[a[pos]]>pos$说明在后面还有一个$a[pos]$,那么删掉这个对个数没有影响,直到$pre[a[pos]]<=pos$,那么删掉这个数,$pos+1~i$这个区间就正好有$j$个数了,更新$b[j]=pos$,$c[j]=j$,同时更新$f[i]$数组
积累:没有思路想$dp$,优化的时候找$dp$的瓶颈,看是否能用一些数据结构或多记一些东西突破他

#include#include #include #include using namespace std; int n,m,nn,a[40010],b[40010],c[40010],pre[40010],f[40010]; int read() { int aa=0,bb=1;char cc=getchar(); while(cc>'9'||cc<'0'){if(cc=='-') bb=-1;cc=getchar();} while(cc>='0'&&cc<='9'){aa=(aa<<3)+(aa<<1)+(cc^'0');cc=getchar();} return aa*bb; } int main() { n=read();m=read();nn=sqrt(n); for(int i=1;i<=n;i++) a[i]=read(); memset(pre,-1,sizeof(pre)); for(int i=1;i<=n;i++){ f[i]=f[i-1]+1; for(int j=1;j<=nn;j++) if(pre[a[i]]<=b[j]) c[j]++; pre[a[i]]=i; for(int j=1;j<=nn;j++){ if(c[j]>j){ int pos=b[j]+1; while(pre[a[pos]]>pos) pos++; b[j]=pos;c[j]--; } f[i]=min(f[i],f[b[j]]+j*j); } } printf("%d\n",f[n]); return 0; }
T3 骆驼
咕咕咕