codeforces 1256F Equalizing Two Strings 题解

Codeforces Round #598 (Div. 3) F

首先无论反转多长的子串 最后的效果能够等同于进行若干次相邻两个字母交换
把相邻交换看作后者前移

abcd->abdc->adbc->dabc
dabc->dacb->dcab
dcab->dcab
dcba

接下来分类讨论

  • 如果有字母在S和T中出现次数不同

显然 \(\sf{NO}\) 这还需要解释吗

  • 如果S和T中存在一个字母出现不止一次

把 S 中这两个字母想办法变成相邻 这一定能做到
然后每次操作交换 S 的这两个相邻字母 T就可以任意交换
S 不变 T 一定可以变成 S

因为可以看作每一个字符任意右移 每次把需要移动的移过去就行了

  • 其它情况

我们可以先想办法把 S 变成升序 同时对T任意选择两个相邻字母每次交换这两个 这一步的次数取决于 S 中逆序对的个数 假设这个数为\(x\)
这样之后再把 T 变成升序 假设 T 中逆序对个数为\(y\) 这一步需要的步数等于\(y+1\)\(y-1\)(\(x\)为奇数) 或 \(y\)(\(x\)为偶数)

如果\(x\)为奇数 在"任意选择两个相邻字母每次交换这两个"操作中 S排序完后T的逆序对显然会增加或减少一个 而如果\(x\)为偶数 这个数不会改变

同时也要在S上进行这些操作(任意选择两个相邻字母每次交换这两个)

可以看出\(x+y\)为偶数是能做到上面一步 否则如果一个为升序 另一个逆序对必会为1

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long LL;

LL T,n;
string s,t;
LL cnt[2][28] = {0};

LL tr[35] = {0};
LL lowbit(LL x){ return (x & (-x)); }
void modify(LL x){ for(;x <= 26;x += lowbit(x)) tr[x] ++; }
LL query(LL x){ LL ret = 0; for(;x;x -= lowbit(x)) ret += tr[x]; return ret; }
// 树状数组求逆序对
// 好像没必要啊

int main(){
	cin >> T;
	while(T --){
		memset(cnt,0,sizeof(cnt));
		cin >> n; cin >> s >> t;
		for(LL i = 0;i < n;i ++){
			cnt[0][s[i] - 'a' + 1] ++;
			cnt[1][t[i] - 'a' + 1] ++;
		}
		
		LL dif = 0; for(LL i = 1;i <= 26;i ++) if(cnt[0][i] != cnt[1][i]) dif = 1;
		if(dif){ cout << "NO" << endl; continue; }
		    // 情况1
      LL sam = 0; for(LL i = 1;i <= 26;i ++) if(cnt[0][i] >= 2) sam = 1;
		if(sam){ cout << "YES" << endl; continue; }
        // 情况2
		
		LL ans = 0,ant = 0;
		memset(tr,0,sizeof(tr));
		for(LL i = 0;i < n;i ++){ ans += query(s[i] - 'a' + 1); modify(s[i] - 'a' + 1); }
		memset(tr,0,sizeof(tr));
		for(LL i = 0;i < n;i ++){ ant += query(t[i] - 'a' + 1); modify(t[i] - 'a' + 1); }
		
		cout << ((ans & 1) == (ant & 1) ? "YES" : "NO") << endl;
	}
	return 0;
}
posted @ 2020-04-21 14:03  ItzInstallB  阅读(133)  评论(0编辑  收藏  举报