2022-ICPC(杭州)K - Master of Both (字典树)

K. Master of Both

题目

Professor Hui-Bot is the master of string theory and advanced data structures, so he came up with an interesting problem. Given a sequence of nn strings consisting of only lowercase English letters, how many inversions are there in this sequence when the strings are compared by lexicographical order?

As the most extraordinary student of Hui-Bot, Putata and Budada mastered superb string theory and advanced data structure skills respectively, and they solved this problem together with ease. However, there are qq different parallel universes, where the characters in the alphabet are not appearing in the original order.

Formally, the alphabet in each universe is a string, which is a permutation of the 2626 lowercase English letter, denoting the order each character appears.

A string aa is lexicographically smaller than a string bb if and only if one of the following holds:

  • aa is a prefix of bb, but a≠ba≠b;
  • in the first position where aa and bb differ, the string aa has a letter that appears earlier in the alphabet than the corresponding letter in bb.

The number of inversions in a sequence aa of length nn is the number of ordered pairs (i,j)(i,j) such that 1≤i<j≤n1≤i<j≤n, aj<aiaj<ai.

Please help Putata and Budada in each universe to solve the problem.

题意

        给n个由小写字母组成的串,给m个询问,每个询问为一个重新定义的字母表,问如果以这个字母表为基准,n个串有多少个逆序对

思路

        如果每次去枚举每一个串显然是不现实的,考虑找两两串之间的比较关系,如果对于一个正常的字母表,比较两个字符串的字典序,首先肯定去找第一位不相同的位数。

        而对于重新定义的字母表,必然也只是去判断第一位不同的字母的字典序先后关系,所以可以考虑把这些需要判断的字母关系先预处理存下来。

        这样就想到字典树,在把每一个串放进字典树的同时,统计需要判断的不同字母之间的位数,最后在每次询问时可以做到快速累加。

        需要注意的是,可能存在不论字母表如何变,一定存在的逆序对,如abcd和abc,所以我们在建字典树的同时也需要处理出那些不需要字母表就能判断出的逆序对。

#include<bits/stdc++.h>
using namespace std;
#define int long long
//#define double long double
typedef long long ll;
const int N = 1e6+100;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f3f3f;

int n,q;
int nxt[N][26];
int cnt[N];
int a[30][30];
int res=0;
int id=0;
void insert(string s) {
	int l=s.size();
	int p = 0;
	for (int i = 0; i < l; i++) {
		int x=s[i]-'a';
		for(int j=0; j<26; j++) {
			if(j==x)continue;
			if(nxt[p][j]!=0) {
				a[x][j]+=cnt[nxt[p][j]];
			}
		}
		if (!nxt[p][x]) nxt[p][x] = ++id;
		p = nxt[p][x];
		cnt[p]++;
	}
	for(int i=0; i<26; i++) {
		if(nxt[p][i]!=0)
			res+=cnt[nxt[p][i]];
	}
}
void solve() {
	cin>>n>>q;
	for(int i=1; i<=n; i++) {
		string s;
		cin>>s;
		insert(s);
	}
	while(q--) {
		string s;
		cin>>s;
		int ans=0;
		for(int i=0; i<26; i++) {
			int x=s[i]-'a';
			for(int j=i+1; j<26; j++) {
				int y=s[j]-'a';
				ans+=a[x][y];
			}
		}
		cout<<ans+res<<'\n';
	}
}

signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t=1;
//	cin>>t;
	while(t--)solve();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心刍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值