1 条题解
-
0
自动搬运
来自洛谷,原作者为

asmend
。搬运于
2025-08-24 22:48:47,当前版本为作者最后更新于2023-08-03 16:06:25,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
考虑对合并过程建一棵树。
对于一个点 ,定义 表示它向上合并的时候,对答案造成的重量贡献的系数。
定义一个点的层级 为它的两个儿子层级的较大值 。我们称 更小的层级为更深的层级。
那么层级为 的非根非叶子节点会对答案造成 的磨损值贡献。由于非根非叶子节点共有 个,可以当成是造成 的贡献,最后给答案减掉 即可。
考虑这样一个过程:从浅往深(从大往小)扫每一层 ,找出所有 但 的点 ,那么我们只关心可重集 以及层数 的点的磨损值贡献之和(记作 )。
考虑从 层转移到第 层时会发生什么,这相当于选择一些叶子把它们分裂成两个叶子。也就是选择 的一个子集 ,对每个 ,将 加入到 ,然后令 。
然后我们发现两个性质。一个是 一定是 的一个前缀,这是显然的。另一个是对于一个方案, 从浅到深单调不降,否则我们可以把某个点留到更深的层级再分裂。
于是可以考虑搜索,每次暴力枚举 ,一共有划分数种方案,可以获得 75 分。
然后我们发现,对于 相同的状态,只有 最小的那个是有用的,可以 bfs 搜出所有状态。实测 的时候只有 个状态,可以通过。
int lim; int t,_,n[15],m,i,j,k,a[15][105],vis[500005],lst[500005]; i64 ans,dis[500005]; int ch[500005][105],cnt; vector<int> seq[500005]; vector<int> f[105]; int qid(vector<int> v) { int x=0,i,len=0; vector<int> cur; ff(v,it){ cur.push_back(*it); if(!ch[x][*it]){ ch[x][*it]=++cnt; f[cur.size()].push_back(cnt); seq[cnt]=cur; dis[cnt]=1e18; } x=ch[x][*it]; } return x; } void upd(vector<int> v,i64 c,int l) { int x=qid(v); if(dis[x]>c){ dis[x]=c;lst[x]=l; } if(dis[x]==c){ lst[x]=max(lst[x],l); } } int main() { //cerr<<sizeof(ch)/1048576<<endl; read(t);fz1(i,t){read(n[i]);fz1(j,n[i])read(a[i][j]);lim=max(lim,n[i]);} upd({0},0,0); fz1(_,lim)for(int x:f[_]){ i64 cur=dis[x]*2; vector<int> v=seq[x]; vector<int> nv=v; fz0k(i,v.size()){ if(x!=1) cur+=2;nv.push_back(v[i]+1);int j=nv.size()-1; if(nv.size()>lim) break; while(j&&nv[j]<nv[j-1])swap(nv[j],nv[j-1]),j--; if(i>=lst[x]) upd(nv,cur,i); } } //cerr<<cnt<<endl; fz1(k,t){ ans=1e18;sort(a[k]+1,a[k]+n[k]+1); if(n[k]==1){puts("0");continue;} for(int i:f[n[k]]){ i64 sum=dis[i]; // cerr<<dis[i]<<endl;ff(seq[i],it) cerr<<*it<<' ';cerr<<endl; fz1(j,n[k]) sum+=1ll*a[k][j]*seq[i][n[k]-j]; ans=min(ans,sum); } printf("%lld\n",ans-(n[k]-2)); } return 0; }
- 1
信息
- ID
- 8972
- 时间
- 1500ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者