[BZOJ3289]Mato的文件管理
题目大意
给你\(n\)个数,\(m\)个询问
每次询问一段区间\([l,r]\),可以进行交换相邻两个元素的操作,使这段区间从小到大排序的最小操作次数
\(n,m\leq 50000\),元素值在\(1000000\)内,没有重复元素
解题思路
这题第一感觉,好像和逆序对有关?
那么给出性质:
使区间[l,r]按题意方式从小到大排序的最小操作次数
等价于区间[l,r]的逆序对总数
!
这是一个很强的结论了
尝试给出证明:
首先证明操作次数的下限是逆序对个数
很显然,每次操作(交换相邻两数)能且至多消除一个逆序对
因此下限就是逆序对个数
然后我们证明每次操作都可以消除一个逆序对
同样很显然,假设不存在可以消除的逆序对,这意味着这个序列是有序的!
同时,如果序列不是有序的,那么必定存在一对相邻的逆序对可以消除
于是我们(类似贪心的)取到了最优方案
维护
于是这题就变成求区间逆序对个数了
那么考虑用莫队和权值树状数组求解
所谓权值树状数组,就是维护对应权值出现次数
第i位上的值代表i的出现次数
那么k的前缀和的含义就是不大于k的数一共有几个
莫队最核心的就是得到指针移动对答案的影响
我们考虑在末尾增加一个元素的情况:
那个元素贡献的逆序对个数就是前面比它大的元素个数
看样子需要两个权值树状数组,一个维护前缀和,一个后缀和?
其实不用,因为比A大的元素个数就是总数-不大于A的元素个数
这个可以直接用权值树状数组维护前缀和
复杂度分析,\(Blo=\sqrt{n}\)最优
复杂度\(O(n\sqrt{n}logn)\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
const int Base=230;
int Blo[70000];
int n,q;
namespace tarr{
int T[100000];
void clear(){memset(T,0,sizeof(T));}
inline int lowbit(int k){return k&-k;}
void add(int K,int v){
while (K<=n){
T[K]+=v;
K+=lowbit(K);
}
}
int query(int K){
int ret=0;
while (K){
ret+=T[K];
K-=lowbit(K);
}
return ret;
}
}
struct query{
int x,y;
int id;
}Q[100000];
bool cmp_q(query X,query Y){return Blo[X.x]<Blo[Y.x]||(Blo[X.x]==Blo[Y.x]&&X.y<Y.y);}
struct val{
int V,id;
}V[100000];
bool cmp_v(val A,val B){return A.V<B.V;}
int U[100000],valcnt;
int L,R,val;
int ans[100000];
int main(){
tarr::clear();
for (int i=0;i<=50000/Base;i++)
for (int j=1;j<=Base;j++)
Blo[i*Base+j]=i;
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&V[i].V),V[i].id=i;
std::sort(V+1,V+n+1,cmp_v);
for (int i=1;i<=n;i++){
if (V[i].V!=V[i-1].V) valcnt++;
U[V[i].id]=valcnt;
}
scanf("%d",&q);
for (int i=1;i<=q;i++)
scanf("%d%d",&Q[i].x,&Q[i].y),Q[i].id=i;
std::sort(Q+1,Q+q+1,cmp_q);
L=R=1;tarr::add(U[1],1);
for (int i=1;i<=q;i++){
while (R<Q[i].y){
R++;
val+=R-L-tarr::query(U[R]-1);
tarr::add(U[R],1);
}
while (L>Q[i].x){
L--;
val+=tarr::query(U[L]-1);
tarr::add(U[L],1);
}
while (R>Q[i].y){
val-=R-L-tarr::query(U[R]-1);
tarr::add(U[R],-1);
R--;
}
while (L<Q[i].x){
val-=tarr::query(U[L]-1);
tarr::add(U[L],-1);
L++;
}
ans[Q[i].id]=val;
}
for (int i=1;i<=q;i++) printf("%d\n",ans[i]);
}