[CCO2020]Exercise Deadlines(贪心)
题面
给一个长度为\(n\)的序列\(d_i(d_i \leq n)\),求满足\(i \leq d_{p_i}\)的排列\(p\)中逆序对数最小值
分析
一开始我们令\(p_i=i\),从后往前调整,并计算每一个任务移到当前位置需要的交换次数。贪心考虑\(d_i\)较大的任务对位置限制较松,应该放到最后面,把任务按照\(d_i\)排列,按照\(d_i\)从大到小贪心。又因为要逆序对数最小,应该把\(i\)较大的放到更后面。于是需要一个优先队列存储任务,并倒序一个一个加到排列里。对于每个任务,交换的次数\(=\)当前要放到的位置-任务当前在排列里的位置. 这样就得到了\(\mathcal{O}(n^2)\)的做法,可以得到70分
考虑优化,算法的瓶颈在维护每个数当前在排列里的位置。类似[IOI2019]排列鞋子,用树状数组维护编号为\(i\)的任务的位置\(p_i\)。初始时把树状数组中每一位设成1,移动的时候将对应位-1,那么前缀和就是在当前在\(i\)前面任务的个数,就得到了\(i\)的位置.时间复杂度\(\mathcal{O}(n\log n)\)
代码
//这第4个题不讲武德,来骗,来偷袭,这好吗?这不好!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#define maxn 200000
using namespace std;
int n;
struct tree{
int c[maxn+5];
inline int lowbit(int x){
return x&(-x);
}
void add(int x,int v){
for(int i=x;i<=n;i+=lowbit(i)) c[i]+=v;
}
int query(int x){
int ans=0;
for(int i=x;i>=1;i-=lowbit(i)) ans+=c[i];
return ans;
}
}T;
int d[maxn+5];
vector<int>id[maxn+5];
priority_queue<int>q;
int main(){
#ifndef LOCAL
freopen("ddl.in","r",stdin);
freopen("ddl.out","w",stdout);
#endif
// freopen("d1p2.2-36.in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&d[i]);
id[d[i]].push_back(i);
}
for(int i=1;i<=n;i++) T.add(i,1);
long long ans=0;
for(int i=n;i>=1;i--){
for(int j=0;j<(int)id[i].size();j++) q.push(id[i][j]);
if(q.empty()){
ans=-1;
break;
}
int x=q.top();
q.pop();
ans+=i-T.query(x);
T.add(x,-1);
}
printf("%lld\n",ans);
}
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢