【AT5162】[AGC037E] Reversing and Concatenating(贪心)

点此看题面

  • 给定一个长度为(n)的字符串(s)
  • 执行(k)次操作,每次将(s)的反串(s^R)接到(s)后面,从中任意截取一个长度为(n)的字符串作为新的(s)
  • 求最终能得到的字典序最小的串。
  • (nle5000,kle10^9)

最小字符贪心

字典序最小有个显然的贪心,就是让开头最小的字符尽可能多。

一种情况是原串的末尾就有(t)个最小的字符((tge 1)),那么我们直接翻转原串,然后再以这(2t)个字符为结尾继续翻转,以此类推,最终能得到(t imes 2^k)个最小字符。

另一种情况是先把原串翻转一次,然后找到某段最小的字符(同样假设为(t)个),最终能得到(t imes 2^{k-1})个最小字符。

直接暴力枚举这个位置,与已有答案比较更新即可。

代码:(O(n^2))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 5000
using namespace std;
int n,k;char Mn='z',s[2*N+5];
char ans[N+5],res[N+5];I void Work(char* s,RI k)
{
	RI p=n;W(s[p-1]==Mn) --p;RI l=n-p+1;W(l^n&&k) l=min(l<<1,n),--k;//翻转k次
	RI i;for(i=1;i<=l;++i) res[i]=Mn;for(;i<=n;++i) res[i]=s[p-(i-l)];//l个最小字符,其后的n-l个字符
	for(i=1;i<=n;++i) if(res[i]^ans[i]) {if(res[i]>ans[i]) return;break;}for(i=1;i<=n;++i) ans[i]=res[i];//和已有答案比较
}
int main()
{
	RI i;for(scanf("%d%d%s",&n,&k,s+1),i=1;i<=n;++i) Mn=min(Mn,s[i]),ans[i]=s[2*n-i+1]=s[i];
	if(s[n]==Mn) Work(s,k);if(k) for(i=1;i<=n;++i) s[i+n]==Mn&&(Work(s+i,k-1),0);return puts(ans+1),0;//直接按末尾翻转,翻转一次后枚举位置继续翻转
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT5162.html