题面
你有两个字符串可重集合 S , T S, T S,T,初始时为空。
你要维护这两个字符串集合,支持加入和删除字符串,查询这两个集合的最大权匹配。
定义集合 S , T S, T S,T 的一组匹配方案为,有若干个二元组 ( s i , t i ) (s_i, t_i) (si,ti),其中 s i ∈ S , t i ∈ T , S , T s_i ∈ S, t_i ∈ T,S, T si∈S,ti∈T,S,T 中的每个元素最多出现在一个二元组中。
定义集合
S
,
T
S, T
S,T 一组匹配方案的权值为,$ \sum lcp(s_i, t_i)$,其中 lcp
表示最长公共前缀。
数据范围
对于 20 % 20 \% 20% 的数据,保证 q ≤ 1000 q \leq 1000 q≤1000,任意时刻 ∣ S ∣ , ∣ T ∣ ≤ 1 |S|, |T| \leq 1 ∣S∣,∣T∣≤1。
对于 50 % 50 \% 50% 的数据,保证任意时刻 ∣ S ∣ , ∣ T ∣ ≤ 3 |S|, |T| \leq 3 ∣S∣,∣T∣≤3。
另有 30 % 30 \% 30% 的数据,保证 q ≤ 80 , ∑ ∣ s t ∣ ≤ 500 q \leq 80, \sum |st| \leq 500 q≤80,∑∣st∣≤500。
对于 100 % 100 \% 100% 的数据,保证 q ≤ 500000 , ∑ ∣ s t ∣ ≤ 2 × 1 0 6 q \leq 500000, \sum|st| \leq 2 \times 10^6 q≤500000,∑∣st∣≤2×106,所有字符均为小写字母。
题解
看到这道题里面有
l
c
p
lcp
lcp,我们就可以考虑往
t
r
i
e
trie
trie 树上去考虑,于是便有了如下:
把每个字符串压入
t
r
i
e
trie
trie 树,然后考虑每个字符的贡献,如果另一个集合内的当前位置所拥有的串比这个集合内的多,那么这个字符就是有贡献的,贡献为1.
删除也同理,于是答案就可以动态维护了。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int n,q,t[2][26][N],qu1[N],nxt[N][26],sz[2][N],tot,ans,k;
string qu2[N];
void insert(int pos,string ck){
k=0;
for(int i=0;i<ck.size();++i){
if(!nxt[k][ck[i]-'a'])nxt[k][ck[i]-'a']=++tot;
k=nxt[k][ck[i]-'a'];
if(sz[pos^1][k]>sz[pos][k])ans++;
sz[pos][k]++;
}
}
void del(int pos,string ck){
k=0;
for(int i=0;i<ck.size();++i){
k=nxt[k][ck[i]-'a'];
if(sz[pos][k]<=sz[pos^1][k])ans--;
sz[pos][k]--;
}
}
int main(){
ios::sync_with_stdio(false);
cin>>q;
for(int i=1;i<=q;++i){
int type,p;string ck;char x;
cin>>type;
if(type==1){
cin>>x>>ck;
if(x=='T')p=1;
else p=0;
//cout<<"FAQ "<<endl;
insert(p,ck);
qu1[i]=p,qu2[i]=ck;
}
else{
cin>>p;
//cout<<"FAQ "<<qu1[p]<<" "<<qu2[p]<<endl;
del(qu1[p],qu2[p]);
}
printf("%d\n",ans);
}
return 0;
}
/*
4
1 S aaab
1 T abccab
1 T aaab
2 3
*/