T1:农场
题意:有一个长为 $n$ 的序列 $a$,要求将其分成尽可能多的部分,使得每一部分的 $a_i$ 的和相等。求最多能分成的部分数。
$30\%:1\le n\le 1000$
$80\%:1\le n\le 10^5$
$100\%:1\le a_i\le 10,1\le \sum a_i\le 10^6$
这题不难,说一下我在考场的思路:
首先答案应该是 $\sum a_i$ 的约数。那么可以转化一下,变成找到满足要求的最小的和(也是其约数)
进一步想到前缀和。我们发现 $x$ 满足条件,当且仅当 $x,2x,3x\dots$ 全部在前缀和中出现。
于是考场上写了个80分的暴力 $O(n\sqrt{n})$(枚举约数 $O(\sqrt{n})$,判断 $O(n)$)
后来发现可以做到更快:因为总和不超过 $10^6$,因此可以开桶。复杂度 $O(\sigma(n))<O(n\log n)$。
但是 $O(n\sqrt{n})$ 可以过?
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n,sum; 4 int fac[1010],fl; 5 bool vis[1000100]; 6 void split(int x){ //求出总和的所有约数 7 for(int i=1;i*i<=x;i++) 8 if(x%i==0){ 9 fac[++fl]=i; 10 if(i*i!=x) fac[++fl]=x/i; 11 } 12 sort(fac+1,fac+fl+1); 13 } 14 int main(){ 15 scanf("%d",&n); 16 for(int i=1;i<=n;i++){ 17 int a; 18 scanf("%d",&a); 19 sum+=a; 20 vis[sum]=true; //对前缀和开桶 21 } 22 split(sum); 23 for(int i=1;i<=fl;i++){ 24 bool flag=true; 25 for(int j=fac[i];j<=sum;j+=fac[i]) //判断是否满足 26 if(!vis[j]){ //不满足 27 flag=false;break; 28 } 29 if(flag){ 30 printf("%d\n",sum/fac[i]);return 0; //答案为总和/单个和=段数 31 } 32 } 33 }