hdu2838 cow sorting用树状数组求逆序对

时间:2022-03-29 04:57:49

题目链接:http://icpc.njust.edu.cn/Problem/Hdu/2838/

题目解法:题目给出一个1-n的排列,操作只有一种:交换相邻的元素,代价是两个元素之和,问将该序列变成升序排列的最小代价。就是要在线求解,每输入一个数a就要查询之前的数中有多少数比a大,这些数由于在a的前面,所以都会与a发生一次交换,否则a无法排在他们前面。假设a前面有k个数比a大,则代价之一是k*a。另一部分代价来自于交换这些数时前面的比a大的数的和。我们可以用树状数组高效的求出截止a位置的逆序对数量,并且可以用另一个树状数组维护比a小的数的和,这个操作很简单,只需要没次输入x时在x位置加上x就行,然后对a求前缀和就可得比a小的所有的数的和,由于所有的数都是小于等于n的,所以大于a的数的和是小于等于n的数的和减去小于等于x的数的和(容斥原理)。最后要注意的一点是要用long long整数。

代码如下:

 #include<bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
typedef long long ll;
typedef unsigned long long ull;
#define pf printf
#define mem(a,b) memset(a,b,sizeof(a))
#define prime1 1e9+7
#define prime2 1e9+9
#define pi 3.14159265
#define scand(x) scanf("%llf",&x)
#define f(i,a,b) for(int i=a;i<=b;i++)
#define scan(a) scanf("%d",&a)
#define dbg(args) cout<<#args<<":"<<args<<endl;
#define pb(i) push_back(i)
#define ppb(x) pop_back(x)
#define inf 0x3f3f3f3f
#define maxn 100010
int n,m,t;
ll sum[maxn],c[maxn];
ll lowbit(ll x)
{
return x&(-x);
}
void update(int x,int y)
{
for(int i=x;i<=n;i+=lowbit(i))
{
c[i]+=y;//c[i]=1时表示插入数i
sum[i]+=x;//sum[i]:可以理解为在i位置上放i
}
}
ll query(int x)//求出小于等于x的所有数之和
{
ll ans=;
for(int i=x;i;i-=lowbit(i))
{
ans +=sum[i];
}
return ans;
}
ll query2(int x)//求出比x小的数的个数
{
ll ans=;
for(int i=x;i;i-=lowbit(i))
{
ans+=c[i];
}
return ans;
}
int main()
{
//freopen("input.txt","r",stdin);
//freopen("output.txt","w",stdout);
std::ios::sync_with_stdio(false);
scan(n);
int x;
ll ans= ;
f(i,,n)
{
scan(x);
ll tmp=i--query2(x-);//选出比x大的数的个数,tmp=0时表示i是当前最大的数,所以不需要
//dbg(tmp);
if(tmp!=)
{
ans+=tmp*x;
ans+=query(n)-query(x);//选出比x大但是小于等于n的数(因为编号是连续的,所以最大是n)
}
update(x,);
}
pf("%lld\n",ans);
}