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)\) 的值

有一个小细节,就是在处理一个串是另外一个串的前缀的时候,是无法处理的,比如 aaabaa 也是逆序对,这就需要在处理 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;
}