2020CCPC绵阳/gym102822 C. Code a Trie
题意
随机生成一颗字典树,字典树上每个结点的权值都互不相同,给定(n)个询问串和返回值,问字典树中至少有多少个结点。
分析
-
首先我们根据所有query建立字典树并记录(sz[x])表示有多少个询问串经过点(x),那么对于同一个串,答案一定是同一个。
-
我们对于每一个值
- 计算对应串的LCA, 然后把LCA标记一下
- 标记这些串在LCA下面的那个点为一定不存在
-
每个点只能被一个值标记为LCA,根到LCA一定不能经过不存在的点
-
dfs贪心计算每个子树最少的节点
-
如果这个点的(sz[x]>=2)
-
这个点一定要选
-
如果这个点是LCA,那么它的儿子结点都要选
-
如果这个点不是LCA,它的儿子中(sz[x]=1)的结点至多有一个不选
-
-
如果这个点的(sz[x]=1),它的儿子一定不选
-
Code
#include<bits/stdc++.h>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=5e5+10;
const int inf=1e9;
int T,n;
string s[N];
int a[N],b[N],cnt[N],vis[N],ans;
int tot,ch[N][30];
vector<string>g[N];
bool input(){
cin>>n;
int flag=1,m=0;
rep(i,1,n){
cin>>s[i]>>a[i];
m+=sz(s[i]);
}
tot=0;
rep(i,1,n) g[i].clear();
rep(i,0,m+5) memset(ch[i],0,sizeof ch[i]);
memset(cnt,0,sizeof(int)*(m+5));
memset(vis,0,sizeof(int)*(m+5));
return flag;
}
bool gao(vector<string> &v){
//寻找LCA
sort(v.begin(), v.end(),[](string x,string y){return sz(x)<sz(y);});
int len=0;
for(int i=0;i<sz(v[0]);i++){
int flag=1;
for(int j=0;j<sz(v)&&flag;j++){
if(v[j][i]!=v[0][i]) flag=0;
}
if(!flag) break;
else len=i+1;
}
//插入前缀
int rt=0;
for(int i=0;i<len;i++){
cnt[rt]++;
int c=v[0][i]-'a';
if(ch[rt][c]==-1) return false;
if(!ch[rt][c]) ch[rt][c]=++tot;
rt=ch[rt][c];
}
//标记LCA
if(vis[rt]) return false;
vis[rt]=1;
cnt[rt]++;
//标记不存在结点
for(int i=0;i<sz(v);i++) if(sz(v[i])>len){
int c=v[i][len]-'a';
if(ch[rt][c]>0) return false;
ch[rt][c]=-1;
}
return true;
}
void dfs(int u){
if(cnt[u]>1) ans++;
int flag=vis[u]==0;
for(int i=0;i<26;i++){
int x=ch[u][i];
if(x==0||x==-1) continue;
if(cnt[x]==1){
if(!flag) ans++;
else flag=0;
}else dfs(x);
}
}
void solve(int cas){
int m=0;
rep(i,1,n){
b[++m]=a[i];
}
sort(b+1,b+m+1);
m=unique(b+1,b+m+1)-b-1;
rep(i,1,n){
a[i]=lower_bound(b+1,b+m+1,a[i])-b;
g[a[i]].pb(s[i]);
}
for(int i=1;i<=m;i++) if(!gao(g[i])){
printf("Case #%d: -1
",cas);
return;
}
ans=0;
cnt[0]++;//根节点必须选
dfs(0);
printf("Case #%d: %d
",cas,ans);
}
int main(){
ios::sync_with_stdio(false);
cin>>T;
for(int cas=1;cas<=T;cas++){
input();
solve(cas);
}
return 0;
}