做题笔记:洛谷P2448
前置芝士
前缀和,离散化,树状数组。
正文
首先显然要将位置变动的数离散化
然后考虑将答案分成两部分:
1.位置变动过的数彼此之间有多少对逆序对
2.位置变动过的数与没变动过的数之间有多少对逆序对
对于第一个问题可以直接在离散化后用树状数组解决
第二个问题需要思考:
不妨考虑 \(i\) 号位置在交换结束后里面的数由 \(i\) 变成了 \(x\)
若 \(i>x\) :
则说明 \(x\) 从 \(i\) 的前面经过那么多交换操作后到了 \(i\) 这里
设 \(x\) 与 \(i\) 之间有 \(k\) 个位置没变动过的数,显然ans+=k,
因为 \(x\) 至 \(i\) 中的数都大于 \(x\) ,所以每一对逆序对是
左端为 \(x\) 至 \(i\) 中任意一个位置没变动过的数,右端是 \(x\) ,共 \(k\) 对
若 \(i<x\) :
则说明 \(x\) 从 \(i\) 的后面经过那么多交换操作后到了 \(i\) 这里
显然还是ans+=k,这次是因为 \(i\) 至 \(x\)中的数都小于x,
所以这时每一对逆序对是左端是 \(x\) ,右端为 \(i\) 至
\(x\) 中任意一个位置没变动过的数,总共还是 \(k\) 对
综上,只要求出交换后的序列,对于每一个位置都有:
ans+= 原位置上的数与现位置上的数之间的未变动元素个数
对于求任意一段区间内的未变动元素个数,可以用前缀和解
决
最后输出即可。
AC code
//writer:Oier_szc
#include <bits/stdc++.h>
#define int long long
using namespace std;
//const int N=;
int k,ans=0;
int a[200005],all[200005],ys[200005],tot;
//离散化用的
int d[200005];
//交换后位置
int tr[200005];
//树状数组
int qzh[200005];
//前缀和
int lowbit(int x)
{
return x&(-x);
}
void update(int u,int x)
{
for(int i=u;i<=tot;i+=lowbit(i)) tr[i]+=x;
}
int query(int u)
{
int res=0;
for(int i=u;i!=0;i-=lowbit(i))
{
res+=tr[i];
}
return res;
}
//上面三个函数为树状数组基本操作
void lsh()
{
sort(all+1,all+1+2*k);
tot=unique(all+1,all+1+2*k)-all-1;
for(int i=1;i<=2*k;++i)
{
ys[i]=lower_bound(all+1,all+1+tot,a[i])-all;
}
}
//离散化
void work()
{
for(int i=1;i<=tot;++i)
{
d[i]=i;
}
for(int i=1;i<=k;++i)
{
swap(d[ys[i]],d[ys[i+k]]);
}
}
//求交换后的序列
void get_ans()
{
for(int i=tot;i>=1;--i)
{
ans+=query(d[i]-1);
update(d[i],1);
}
//求解问题一
for(int i=1;i<=tot;++i)
{
qzh[i]=qzh[i-1]+all[i]-all[i-1]-1;
}
//对未变动过的数前缀和
for(int i=1;i<=tot;++i)
{
if(d[i]>i) ans+=qzh[d[i]]-qzh[i];
else ans+=qzh[i]-qzh[d[i]];
}
//求解问题二
}
signed main()
{
scanf("%lld",&k);
int x,y;
for(int i=1;i<=k;++i)
{
scanf("%lld%lld",&x,&y);
a[i]=x,a[i+k]=y;
all[i]=x,all[i+k]=y;
//这里用a[1~k]表示x,a[k+1~2k]表示y
}
lsh();
work();
get_ans();
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号