P6170题解

· · 题解

题目传送门

一道很好的数论题。

题意:

有 n 头奶牛,分布在一些房间,某些房间可能有多头牛,要让这些牛按顺时针移动,使每一个房间恰好有一个奶牛的最小花费。

花费计算:如果一头奶牛穿过了 d 扇门,他消耗的能量为 d^2 。

思路:

对于这道题,我可以直说,用暴力就行了,根本不需要其他的技巧。

首先说一个东西:如果有 1 头奶牛在 a 点,1 头奶牛在 b 点,还有一个没有奶牛的 c 点,且 c>b>a,要想有一头奶牛在 b 点,一头奶牛在 c 点,方案 a \to b,b \to c 比方案 a \to c 好。

因为 \sum c_i = n,且要使得 \forall c_i = 1,每头奶牛走的步数最少,所以可以直接从第 1 个房间开始遍历,如果 c_i > 1 ,就把这个房间的 (c_i-1) 头奶牛移到后面的一个房间,留在这个房间里的奶牛一定是步数走的最多的,所以不用判断每头奶牛的新旧,以此类推,直到满足 \forall c_i =1,循环结束。

比如说:

样例数据 : 10 1 0 0 2 0 0 1 2 2 2.

明显最优的方案是:

如果一个一个走的话,时间复杂度是 $O(n^2)$,不会爆。 但是如果全部走的话,时间复杂度是 $O(n)$,刚好。 ## 贴贴代码 ```cpp #include <bits/stdc++.h> #define ll long long using namespace std; struct node{ll N,S,W;}a[101000]; // N 当前房间的下一个房间 // S 当前房间还有多少头牛 // W 当前房间还有多少头牛 ll n,ans,p=1; int main(){ cin>>n; for(ll i=1;i<=n;i++) cin>>a[i].S; for(ll i=1;i<=n;i++) a[i].N=i+1; a[n].N=1; // 记录当前房间的下一个房间的编号 for(ll i=1;i<=2*n;i++){ if(a[p].S>1){ a[a[p].N].S+=a[p].S-1; a[a[p].N].W+=a[p].S-1; a[p].S=1; } p=a[p].N; } for(ll i=1;i<=n;i++) ans+=(a[i].W*a[i].W); printf("%lld",ans); return 0; } ``` 时间复杂度还挺低 , $O(n)$。 ~~这样你就能一下 A 两道题啦~~