题意:现在要给站成一排的n头牛挤奶,每头牛都要么面朝左要么面朝右。在给一头牛A挤奶的时候,未被挤奶并且面朝A的所有奶牛都会受到惊吓而奶质量降1,可以认为每头牛的奶质量足够大(每次都够减)。问选择最佳的挤奶顺序使得总质量损失最小,输出最小的损失量。
解法:就是一道思维题,可以这样理解。我们把一个矛盾对定义为头对头两头牛,他俩一个向右另一个向左并且相互看到对方。那么仅仅对这一对来说,无论顺序如何,质量都会损失1,不会多也不会少。对于非矛盾对的向左向右的两头牛,无论顺序如何,质量都不会有损失。也就是说,对于向左的牛来说,无论向右牛挤奶顺序如何,都不会有什么区别,反之一样。然后再看,在所有向左的奶牛中,一定是从右向左挤为最佳顺序,在所有向右的奶牛中,一定是从左向右挤为最佳顺序。所以,现在结论就出来了:从左向右,将所有面朝右的奶牛挤了,这时所有的损失等于将每头面朝右奶牛左边面朝左的奶牛的数量加起来。然后再从从右向左,将所有面朝左的奶牛挤了,这期间的质量损失是0.代码超短。但是自己在比赛时候没有想到这些,搞了半天YY了个nlogn树状数组乱搞的;100行代码,wa了两发,最后一分钟才补交正确通过。悼念一下我那逗比的YY代码吧:
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
using namespace std;
int C1[200010];
int C2[200010];
int n;
int lowbit(int x)
{
return x&(-x);
}
void update1(int x)
{
while(x<=n)
{
C1[x]++;
x+=lowbit(x);
}
}
int query1(int x)
{
int ans=0;
while(x)
{
ans+=C1[x];
x-=lowbit(x);
}
return ans;
}
void update2(int x)
{
while(x<=n)
{
C2[x]++;
x+=lowbit(x);
}
}
int query2(int x)
{
int ans=0;
while(x)
{
ans+=C2[x];
x-=lowbit(x);
}
return ans;
}
bool rem[200010];
struct point
{
int num,position;
bool de;
} ;
point points[200010];
bool operator<(point a,point b)
{
return a.num<b.num;
}
int num[200010];
int sum1[200010];
int sum2[200010];
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
cin>>rem[i];
if(rem[i])num[i]=n-i,sum1[i]=sum1[i-1]+1,sum2[i]=sum2[i-1];
else num[i]=i-1,sum1[i]=sum1[i-1],sum2[i]=sum2[i-1]+1;
points[i].num=num[i],points[i].position=i;
points[i].de=rem[i];
}
sort(points+1,points+n+1);
reverse(points+1,points+n+1);
long long ans=0;
int left=0,right=0;
for(int i=1;i<=n;i++)
{
int k=points[i].position;
if(rem[points[i].position])
update2(points[i].position);
else
update1(points[i].position);
if(rem[points[i].position])right++;
else left++;
}
cout<<ans<<endl;
return 0;
} int tool1=query1(points[i].position-1);
int tool2=query2(points[i].position-1);
ans+=sum1[k-1]-tool2+sum2[n]-sum2[k]-(left-tool1);
if(rem[points[i].position])
right++;
else
left++;
}
cout<<ans<<endl;
return 0;
}