求 ∑ni=1|ai−x| ∑ i = 1 n | a i − x | 的最小值。
如果x<中位数,x后面的数的增加量大于前面的数的减少量
反之亦然。
所以x为中位数。
ljt神犇:
将 (i,ai) ( i , a i ) 投影到坐标平面上
然后用直线 x=k x = k 去扫,可以想象到在上下点分布平均时距离总和最小。
luoguP1031的问题非常简单,做法也很清晰。首先维护一个平均值ave,然后依次从右面移过去。
然后我们模拟一遍即可。举个栗子,3 5 1 7 平均数是4,那么先变成4 4 1 7然后变成4 4 4 4
有个问题,譬如1 1 5 9这样的话,变成的是4 -2 5 9,会不会出锅?并不会,因为我们可以发现,-2相当于调整了一下顺序,先把5的移过来再进行下一步操作。
所以直接模拟一下即可。
//一年前写的代码2333
#include
using namespace std;
int main(){
int i;
cin>>i;
int pai[i],s=0;
for(int p=0;pcin>>pai[p];
s+=pai[p];
}
int avg=s/i;
int f=0;
for(int p=0;pint j=avg-pai[p];
if(j!=0){
pai[p+1]-=j;
pai[p]=avg;
++f;
}
}
cout<
但lyd并没有局限于这一模拟过程。假如我们将状态进行抽象,我们会得到一些普遍性的结论。
假设总和为 T T ,有 M M 张纸牌。设 ave=TM a v e = T M 。对于第i个人来说,如果 C[i]<ave C [ i ] < a v e 他拿的应该是 C[i]+ave C [ i ] + a v e 张,后一个拿 C[i+1]−ave+C[i] C [ i + 1 ] − a v e + C [ i ] ,否则,他拿 C[i]−ave C [ i ] − a v e 张,而后一个拿 C[i+1]+C[i]−ave C [ i + 1 ] + C [ i ] − a v e 张。
但是这样并不方便维护,我们考虑整体和隔离的思想。将前i个看做一个整体,显然前i个内部的均分是不会改变其整体结构的,因而对于该体系来说,想要达到平均数结构,就必须与下一个体系交换足够的纸牌,而交换数量就是 |G[i]−i⋅ive| | G [ i ] − i ⋅ i v e | ,其中 G[i] G [ i ] 是前缀和。然后就可以推出一个结论: d=∑Mi=1|i⋅ave−G[i]| d = ∑ i = 1 M | i ⋅ a v e − G [ i ] | ,也就是将每次体系更新的贡献加起来。
如果让每个人的数量都减去 ave a v e ,结果就可以经过简单的数学推导进一步化简: d=∑Mi=1|S[i]| d = ∑ i = 1 M | S [ i ] | ,其中 S[i] S [ i ] 是新数组的前缀和。这就是均分纸牌问题的通用公式。
现在考虑一种变形:如果这里的纸牌是环形的呢?
对于环形问题,首先考虑切开。假定我们切开的东西是 A[k+1],A[k+2],...,A[M],A[1],...,A[k] A [ k + 1 ] , A [ k + 2 ] , . . . , A [ M ] , A [ 1 ] , . . . , A [ k ] ,那么其前缀和也会有所变化,即 S[k+1]−S[k],S[k+2]−S[k],...,S[M]−S[k],.S[1]+S[M]−S[k],...,S[M] S [ k + 1 ] − S [ k ] , S [ k + 2 ] − S [ k ] , . . . , S [ M ] − S [ k ] , . S [ 1 ] + S [ M ] − S [ k ] , . . . , S [ M ]
由于均分之后, S[M]=0 S [ M ] = 0 恒成立,所以前缀和的变化仅仅是减去 S[k] S [ k ] 。那么,我们要求的就是哪个取值上最短,换言之,求什么时候 ∑Mi=1|S[i]−S[k]| ∑ i = 1 M | S [ i ] − S [ k ] | 取到最小。
等等,这个形式有点熟悉的样子。。这不就是货仓选择得到的中位数么!!!
因此,我们就知道一个结论:对于环形均分纸牌问题来说,最小步数在 S[k] S [ k ] 为中位数所对应的 k k 处切开取到最小值。
http://begin.lydsy.com/JudgeOnline/problem.php?id=4711
这里可以提交。。
在两个方向上跑一遍环形均分纸牌即可。
#include
#include
#include
#include
#include
using namespace std;
#define N 100003
#define LL long long
LL n, m, T;
LL row[N], col[N], s[N];
LL avr, avc, ans;
inline void read(LL& x) {
x = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
int main() {
read(n), read(m), read(T);
LL x, y;
for(int i = 1; i <= T; ++i) {
read(x), read(y);
row[x]++, col[y]++;
avr++; avc++;
}
if(avr % n == 0 && avc % m == 0) printf("both ");
else if(avr % n == 0) printf("row ");
else if(avc % m == 0) printf("column ");
else return printf("impossible\n"), 0;
if(avr % n == 0) {
avr /= n;
for(int i = 1; i <= n; ++i) row[i] -= avr, s[i] = s[i - 1] + row[i];
sort(s + 1, s + n + 1);
LL k = s[n / 2 + 1];
for(int i = 1; i <= n; ++i) ans += abs(s[i] - k);
}
memset(s, 0, sizeof s);
if(avc % m == 0) {
avc /= m;
for(int i = 1; i <= m; ++i) col[i] -= avc, s[i] = s[i - 1] + col[i];
sort(s + 1, s + m + 1);
LL k = s[m / 2 + 1];
for(int i = 1; i <= m; ++i) ans += abs(s[i] - k);
}
cout<return 0;
}
总之就是用一个大根堆和一个小根堆,非常巧妙。
#include
#include
#include
using namespace std;
priority_queue<int> big;
priority_queue<int, vector<int>, greater<int> > small;
int main() {
//freopen("test.txt", "w", stdout);
int T;
scanf("%d", &T);
int id, m, cnt, a, mid;
while(T--) {
while(!big.empty()) big.pop();
while(!small.empty()) small.pop();
scanf("%d%d", &id, &m);
cnt = 1, mid = -0x3fffff;
cout<" "<<(m + 1 >> 1)<for(int i = 1; i <= m; ++i) {
scanf("%d", &a);
if(big.empty()) {
big.push(a);
mid = a;
cout<" ";
continue;
}
if(a < mid) big.push(a);
else small.push(a);
if(((int)big.size() - (int)small.size()) > 1) {
//cout<
int t = big.top();
big.pop();
small.push(t);
} else if(((int)small.size() - (int)big.size()) > 1) {
int t = small.top();
small.pop();
big.push(t);
}
if(i & 1) {
++cnt;
if(big.size() > small.size()) mid = big.top();
else mid = small.top();
if(cnt % 10 == 0) cout<else cout<" ";
} else mid = (small.top() + big.top()) / 2;
}
if(cnt % 10 != 0) cout<return 0;
}
mmp下次再不注释文件我tm剁手
blog似乎要迁移了的样子…