CSP 2019.11.14 密码

题目描述

哪里有压迫,哪里就有反抗。
moreD 的宠物在法庭的帮助下终于反抗了。作为一只聪明的宠物,他打算把
魔法使 moreD 的魔法书盗去,夺取 moreD 的魔法能力。但 moreD 怎么会让自己的
魔法书轻易地被盗取?moreD 在魔法书上设置了一个密码锁,密码锁上有一个问
题。
施以斯卧铺魔法吧,你有 M 次机会,如此将得完美密码。
然后是一串小写字母串。
moreD 的宠物斯卧铺魔法就是施法时的字符串其中相邻两位交换。
而 moreD 对于完美密码的定义自然是最小字典序了。
请帮助 moreD 的宠物,想出密码吧。

输入格式

第一行一个整数 M,表示操作次数。
第二行一串小写字母组成的字符串 S,如题目所示。

输出格式

输出完美密码

输入样例

3
dcba

输出样例

adcb

数据范围

对于 30%的数据|S|≤10
对于 60%的数据|S|≤3,000
对于 100%的数据 8≤|S|≤100,000,M≤(|S|-8)^2+2

后记

宠物最终战胜了 moreD,和自己的宠物快乐地生活着。

样例解释

先对第 3,4 两位施法,字符串变成 dcab,然后对第 2,3 两位施法,字符串
变成 dacb,最后对第 1,2 两位施法,字符串变成 adcb。

一眼望过去

很明显是个贪心啊,要求在一定的步骤内通过两两交换使得字典序最小.

解法

针对30%的数据:
暴力dfs。
针对60%的数据:
贪一下心吧。由于题目要求字典序,可以贪心地从前到后确定每一位的字母。字母肯定是从小到大地枚举,操作距离内的枚举字母的话肯定会贪心地把这个字母换入目标位置。而选择枚举的字母就是贪心地选择尽量前的字母。
针对100%的数据:
每次找每种字母最前的那个,再对他进行往前移,这样可以用最小的步骤。可以用vector或链表。然后我们要统计交换的代价,因为是动态改变的,我们就可以用数据结构来维护,比如树状数组或线段树.

参考代码

#include<bits/stdc++.h>
const int N=1e5+10;
typedef long long ll;
using namespace std;
int n;
int top[26];
ll m,tre[N];
char s[N],ans[N];
vector<int>f[26];
inline int lowbit(int k){
	return k&(-k);
}
inline void add(int k,int v) {
	for(int i=k; i<=n; i+=lowbit(i)) tre[i]+=v;
}
inline ll ask(int i) {
	ll sum=0;
	while(i) {
		sum+=tre[i];
		i-=lowbit(i);
	}
	return sum;
}
int main() {
	freopen("pasuwado.in", "r", stdin);
	freopen("pasuwado.out", "w", stdout);
	cin>>m;
	scanf("%s",s+1);
	n=strlen(s+1);
	for(int i=1; i<=n; i++) {
		f[s[i]-'a'].push_back(i);
		if(i==1) add(i,0);
		else add(i,1);
	}
	for(int i=1; i<=n; i++) {
		for(int j=0; j<26; j++) {
			if(top[j]>=f[j].size()) continue;
			int t=f[j][top[j]];
			ll cost=ask(t);
			if(cost<=m) {
				m-=cost;
				add(t+1,-1);
				top[j]++;
				ans[i]=j+'a';
				break;
			}
		}
	}
	for(int i=1; i<=n; i++) printf("%c",ans[i]);
	cout<<endl;
	return 0;
}
posted @ 2019-11-14 13:44  贰冬  阅读(190)  评论(0编辑  收藏  举报