1 条题解
-
0
建出 Trie 树,发现题意就是在串后面加字符,让所有串都在不同叶子上。
如果一个串落在深度为 的点上,把它深度加到 的代价是 。对于非叶节点 ,我们要在它子树内加一个点,并把 放过去。有两种情况:放到叶子下和非叶子节点的旁边。对于第一种,由于我们不能让串有祖先关系,需要把那个叶子也往下放一个,如果该叶子为 ,代价就是 ,对于第二种情况,我们只需改变 的深度,代价就是 。
对于子树树根 ,把 提出来,发现维护 的最小值即可。这样从下往上贪是对的,因为一个点对于所有祖先的贡献一样,早用了不会劣。
如何维护?启发式合并维护 set 就很好写。具体的,我们用小根堆维护子树内 的最小值,把子树 set 合并起来,然后看 是否为叶子节点,如果是,那就插入一个 ,否则插入 。拿最小值更新答案,再删掉最小值。但我们发现一个问题:把 移到 后,上面的点还有可能再移到 的子树里,于是就需要插入 ,但我们无法通过 set 知道 在树上的结构从而确定 ,那么就要多记录一维信息。用 pair 存 以及 ,表示是否需要将 的点也下移,这样就能维护了。
具体实现可以看代码。
#include<bits/stdc++.h> #define pi pair<int,int> #define fs first #define sc second #define for_(a,b,c) for(int a=b;a<=c;++a) using namespace std; int n; const int N=1e6+10; int dep[N],ch[N][2],num[N],tn=1; string cs; void add(){ cin>>cs;cs=' '+cs; int x=1;dep[x]=1; for_(i,1,cs.length()-1){ int op=cs[i]-'0'; if(!ch[x][op])ch[x][op]=++tn; dep[ch[x][op]]=dep[x]+1; x=ch[x][op]; } num[x]++; } multiset<pi>s[N]; int ans=0; void dfs(int x){ if(!x)return; dfs(ch[x][0]);dfs(ch[x][1]); if(!ch[x][0]&&!ch[x][1])s[x].insert({dep[x]+2,1}),--num[x]; else if(!ch[x][1])swap(s[x],s[ch[x][0]]),s[x].insert({dep[x]+1,0}); else if(!ch[x][0])swap(s[x],s[ch[x][1]]),s[x].insert({dep[x]+1,0}); else{ if(s[ch[x][0]].size()>s[ch[x][1]].size()){ swap(s[x],s[ch[x][0]]); for(auto d:s[ch[x][1]])s[x].insert(d); } else{ swap(s[x],s[ch[x][1]]); for(auto d:s[ch[x][0]])s[x].insert(d); } } for_(i,1,num[x]){ auto it=s[x].begin();s[x].erase(it); int d=it->fs;ans+=d-dep[x]; if(it->sc)s[x].insert({d+1,1}),s[x].insert({d+1,2}); else s[x].insert({d+2,1}); } } int main(){ ios::sync_with_stdio(0);cin.tie(0); cin>>n; for_(i,1,n)add(); dfs(1); cout<<ans; return 0; }
- 1
信息
- ID
- 7598
- 时间
- 3000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 24
- 已通过
- 6
- 上传者