xjtuoj 1027: jwp的数学是跟谁学的
不管什么题目先暴力交一遍就完事了
int a[1000005];
int main() {
std::ios::sync_with_stdio(false);
int n;
n=read();
ll ans=0;
#ifdef brute_force
F(i,0,n) a[i]=read();
F(i,0,n)
F(j,i,n)
if(a[i]>a[j])
sad
总之是道金典逆序对题,可以用分治思想或者树状数组基础应用题
分治
主要学习了这篇博客里的方式。
回忆归并排序的实现方式,通过将一个序列划按相邻的数分为n个小段,转换为有序序列后逐步合并成大序列。因此只需要在排列的过程中加上计算逆序对的操作即可。
由于合并中途已经确保两个比较的区间有序,因此当\(a_i>a_{mid+k}\)时,意味着\(a_i\)与\(a_{mid\ldots mid+k}\)都成逆序对
由于\(n_{max}=10^6\),注意答案要用\(long\,long\)(第一次交没改全局longlong第二次没改输出函数里的longlong,激情刷提交记录)
代码
ll marray(int s,int t,int mid,int a[]){
ll i=s,j=mid+1;
int w=0;
ll ans=0;
int *k=new int[t-s+1];
while(i<=mid&&j<=t){
if(a[i]<=a[j]){
k[w++]=a[i++];
}
else{
k[w++]=a[j++];
ans+=mid-i+1ll;
}
}
while(i<=mid){
k[w++]=a[i++];
}
while(j<=t){
k[w++]=a[j++];
}
for(int i=s,j=0;i<=t;i++,j++) a[i]=k[j];
delete[] k;
return ans;
}
ll msort(int s,int t,int a[]){
if(s==t) return 0;
int mid=(s+t)/2;
ll ans=0;
ans+=msort(s,mid,a);
ans+=msort(mid+1,t,a);
ans+=marray(s,t,mid,a);
return ans;
}
树状数组
先开一个数组用于离散化原数组,然后再用树状数组维护下区间出现数字个数就好了(改下求前缀和部分的板子题)
代码
int a[M],d[M],tree[M],n;
ll an;
int lowbit(int x)
{
return x&-x;
}
void add(int x)
{
for(;x<=n;x+=lowbit(x)) tree[x]++;//改成当前区间包含有多少个数字
}
ll sum(int x)
{
ll ans=0;
for(;x;x-=lowbit(x)) ans+=tree[x];
return ans;
}
bool cmpT(int x,int y){
if(a[x]==a[y]) return x>y;
return a[x]>a[y];
}
int main()
{
std::ios::sync_with_stdio(false);
n=read();
#ifdef Fenwick_Tree
F(i,1,n+1){
a[i]=read();
d[i]=i;//Discretization
}
sort(d+1,d+n+1,cmpT);
F(i,1,n+1){
add(d[i]);
an+=sum(d[i]-1);//计算比i小的有多少个
}
cout<<an;
#endif
return 0;
}