题目描述
哪里有压迫,哪里就有反抗。
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; }