luogu P2512 [HAOI2008]糖果传递

传送门

Time cost: 35min

环形均分纸牌

我们再回顾一下均分纸牌

 1     scanf("%d",&n);
 2     for(i = 1; i <= n; i++)
 3     {
 4         scanf("%d",a + i);
 5         sum += a[i];
 6     }
 7     double ave = sum / (double)n;
 8     int tim = n;
 9     sum = 0;
10     for(int i = 1; i <= n; i++)
11     {
12         sum += a[i];
13         if(sum / (double)i == ave) --tim;
14     }

就这么个东西

数组元素减掉平均值之后求前缀和

前缀和为0的时候就说明不用从左边移牌到右边 次数-1

这个题虽然问的是最小传递数 但是效果一样 

开始可以考虑枚举断开的点

如果在k点断开 那么就相当于从k开始求前缀和

但是每遍求前缀和复杂度会爆炸O(n^2)

所以可以从1开始求的前缀和推出来后面的就是

n点的前缀和 S[n]-S[k]

1点的前缀和 S[1]+S[n]-S[k]

(就写这两个应该就知道了)

这样就可以O(n^2) 但是还是会爆炸

然后考虑最后的答案就是所有前缀和求和

也就是

考虑数轴上从1到n 每个S[i]位置上有一个点

求最短距离和

是不是看到了种树问题?

所以就取最中间的点就OK

复杂度O(n)+O(sort)

Code:

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define rep(i,a,n) for(int i = a;i <= n;++i)
 5 const int N = 1000006;
 6 using namespace std;
 7 typedef long long ll;
 8 ll n,ave,ans;
 9 ll a[N],s[N];
10 int main() {
11     scanf("%lld",&n);
12     rep(i,1,n) scanf("%lld",a+i),ave += a[i];
13     ave /= n;
14     rep(i,1,n) a[i] -= ave,s[i] = s[i-1] + a[i];
15     sort(s+1,s+n+1);
16     ave = s[n+1 >> 1];
17     rep(i,1,n) ans += abs(ave - s[i]);
18     printf("%lld\n",ans);
19     return 0;
20 }
View Code

 

posted @ 2018-10-18 16:55  白怀潇  阅读(147)  评论(0编辑  收藏  举报