[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);
}
posted @ 2020-11-25 15:01  birchtree  阅读(84)  评论(0编辑  收藏  举报