ICPC2022Hangzhou F Da Mi Lao Shi Ai Kan De 题解

发布时间 2023-12-03 00:30:47作者: Martian148

Link

ICPC2022Hangzhou F Da Mi Lao Shi Ai Kan De

Question

给出 \(Q\) 个 \(a-z\) 的排序表示比较规则,求 \(n\) 个串在每个比较规则下的逆序对个数

Solution

我们发现,对于两个串的比较来说,决定大小的只是两个字母的比较,比如说

aac
aab

判断这是否是逆序对,起决定性作用的就是比较规则中 \(c\) 在 \(b\) 前面还是 \(b\) 在 \(c\) 前面。然而,前面的 aa 其实对任意的一组比较规则都可以忽略不看,就取决于 \(c\),\(b\) 的顺序

如果我们可以先不看比较规则,提前构造出起决定性作用的那些字符二元组, 定义 \(f(x,y)\) 表示 \(n\) 个串中,起决定性作用的两个字母是 \(x\) 和 \(y\),\(x\) 的串在 \(y\)的串前面,出现的数量。例如前面的数据中 \(x\) 就是 \(c\),\(y\) 就是 \(b\)

如果构造出了 \(f(x,y)\) ,如果一组排列中,\(y\) 在 \(x\) 前面,那么 \(f(x,y)\) 就构成了逆序,要加上 \(f(x,y)\)

关键在于如何构造 \(f(x,y)\)

\(x,y\) 只与不同的那个字符有关,所以考虑字典树,刚开始是一颗空的字典树,往字典树里面加字符串,当遍历到的字典树的节点为 \(pos\) 时,需要加入的下一个字符是 \(s_j\) ,那么对于 \(pos\) 的每一个儿子所对应的序列都在 \(s\) 前面,所以 \(f(son_k,s_j)+=num[sum_k]\) ,\(num[x]\) 表示字典树上 \(x\) 点所包含的串数

image.png

如图,蓝色部分就是在当前串之前已经处理好的串,我需要加入 \(c\) ,那么就要增加 \(f(a,c)\) 和 \(f(b,c)\) 的值

有一个小细节,就是在处理一个串是另外一个串的前缀的时候,是无法处理的,比如 aaab 和 aa 也是逆序对,这就需要在处理 aa 的最后一个字符的时候,加上这个字符的所有字数的 \(num\)

Code

#include<bits/stdc++.h>
using namespace std;
int N,Q;
typedef long long LL;
const int maxn=5e5+5,maxL=1e6+5;
inline int read(){
	int ret=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}

int t[maxL][26],Len=0;
LL num[maxL],now_ans=0,ans2;
string s[maxn];
LL ans[26][26];


void in(string s){
    int siz=s.length(),pos=0;

    for(int i=0;i<siz;i++){
        int now_x=s[i]-'a';
        for(int j=0;j<26;j++){
            if(now_x==j) continue;
            ans[j][now_x]+=num[t[pos][j]];
        }

        if(t[pos][now_x]==0) {
            ++Len;
            t[pos][now_x]=Len;
            pos=Len;
        }

        else {
            pos=t[pos][now_x];
            if(i==siz-1){
                for(int j=0;j<26;j++)
                    ans2+=num[t[pos][j]];
            }
        }
        num[pos]++;
    }
}

int main(){
    N=read(),Q=read();
    for(int i=1;i<=N;i++){
        cin>>s[i];
        in(s[i]);
    }
    for(int i=1;i<=Q;i++){
        now_ans=0;
        string q;cin>>q;
        int siz=q.length();
        vector<int> id(26);
        for(int j=0;j<siz;j++){
            id[q[j]-'a']=j;
        }
        for(int j1=0;j1<26;j1++)
        for(int j2=0;j2<26;j2++){
            if(id[j1]>id[j2]){
                now_ans+=ans[j1][j2];
            }
        }
        printf("%lld\n",now_ans+ans2);
    }
    return 0;
}