CF1526D Kill Anton(暴力)

本文介绍了一种解决字符逆序对问题的方法,通过分析字符Ti和Tj在字符串中的位置关系,确定如何操作以最小化逆序对数量,并提供了C++代码实现。关键在于理解不同字符位置对逆序对的影响并制定策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

我的证明:这似乎非常对啊。
。。。

解析

直观感受:字母交错出现非常愚蠢。
然后就猜对了

为什么?
考虑两个相同但不相邻的字符 T i , T j T_i,T_j Ti,Tj,对应位置为 p i , p j p_i,p_j pi,pj
夹在中间的字符 k k k 无非三种可能。

  1. p i < p k < p j p_i<p_k<p_j pi<pk<pj,此时无论是把i移到后面还是把j移到前面都会增加一个逆序对。
  2. p k < p i < p j p_k<p_i<p_j pk<pi<pj,此时把j移到前面会增加一个逆序对,把i移到后面会减少一个逆序对。
  3. p i < p j < p k p_i<p_j<p_k pi<pj<pk,此时把i移到后面会增加一个逆序对,把j移到前面会减少一个逆序对。

那么我们只需要讨论一下2、3两种情况那种更多,按照对应策略操作,就可以把 T i , T j T_i,T_j Ti,Tj 挪到一起,且逆序对不减少。
证毕。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned ll
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("ok\n")

const int N=1e5+100;
const bool Flag=1;

inline ll read() {
	ll x(0),f(1);char c=getchar();
	while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
	return x*f;
}
bool mem1;

int n;
char s[N],ch[5],ans[N],tmp[N];
int cnt[5],sum[N][5];
int q[5],vis[5];
ll mx;
map<char,int>mp;
queue<int>qq[5];
int f[N];
inline void add(int p,int w){
	for(;p<=n;p+=p&-p) f[p]+=w;
	return;
}
inline int ask(int p){
	int res(0);
	for(;p;p-=p&-p) res+=f[p];
	return res;
}
ll calc(){
	ll res(0);
	for(int i=1;i<=n;i++){
		qq[mp[tmp[i]]].push(i);
		f[i]=i&-i;
	}
	for(int i=1;i<=n;i++){
		int p=qq[mp[s[i]]].front();
		qq[mp[s[i]]].pop();
		int pos=i-1+ask(p);
		add(p,-1);
		res+=pos-i;
	}
	return res;
}
void dfs(int k){
	if(k>4){
		int num(0);
		for(int i=1;i<=4;i++){
			for(int j=1;j<=cnt[q[i]];j++) tmp[++num]=ch[q[i]];
		}
		//printf("%s\n",tmp+1);
		ll o=calc();
		if(o>mx){
			mx=o;
			memcpy(ans,tmp,sizeof(char)*(n+1));
		}
		return;
	}
	for(int i=1;i<=4;i++){
		if(vis[i]) continue;
		vis[i]=1;
		q[k]=i;
		dfs(k+1);
		vis[i]=0;
	}
	return;
}

void work(){
	scanf(" %s",s+1);
	n=strlen(s+1);
	mx=-1;
	memset(cnt,0,sizeof(cnt));
	for(int i=1;i<=n;i++){
		cnt[mp[s[i]]]++;
		for(int j=1;j<=4;j++) sum[i][j]=sum[i-1][j];
		sum[i][mp[s[i]]]++;
	}
	//scanf(" %s",tmp+1);
	//printf("tmp=%lld\n",calc());
	dfs(1);
	
	for(int i=1;i<=n;i++) putchar(ans[i]);
	puts("");	
	//printf("mx=%lld\n",mx);
}

bool mem2;
signed main() {
	#ifndef ONLINE_JUDGE
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	#endif
	ch[1]='A';ch[2]='N';
	ch[3]='O';ch[4]='T';
	mp['A']=1;
	mp['N']=2;
	mp['O']=3;
	mp['T']=4;
	int T=read();
	while(T--) work();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值