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

cyffff
Not yet for the story on the last page, it's not the end.搬运于
2025-08-24 22:43:53,当前版本为作者最后更新于2022-12-22 14:05:40,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意
对于一个 个结点的带边权的树 ,定义 为 中 路径上的边权和。再定义一个 个结点的无向完全图 ,其中 , 中边 的边权为 。
定义 为 的最大生成树。特别的,若 的最大生成树不唯一,请立刻判断出并报告。
给定树 和整数 ,求 。边权为正整数。
若 使得 的最大生成树不唯一,输出 。否则,输出 的所有边权和对 取模的结果。
,。
思路
来补个严谨证明。
考虑 的部分分。
使用 算法。我们对每个点找到距离它最远的点并与其连边。很显然,经过一次 算法,所有的点已经连通。原因是树上每个点距离最远的点为直径两端点之一,而直径两端点互相连接。
假设答案不是 ,我们就已经得到了 了。来判断答案是不是 ,共有以下情况:
- 直径不唯一
我们先证明,不会存在两条互不相交的直径。
反证,考虑存在下面的树,满足 , 为两条直径。

不妨令 ,,。
显然,,,所以 ,。所以 ,矛盾。当 时也易证矛盾。
所以,树不存在两条互不相交的直径。我们还可以证明如果有多条直径,则对于每一条直径,必然有一条其他直径与它在一个端点相交。
于是我们可以找出一条直径 ,算出所有的 和 ,如果有 或 ()则直径不唯一。
直径不唯一时答案也不一定为 。当所有直径有同一个端点(具体为上述两种情况仅满足一个),设为 ,并且所有点到 距离都大于到另一个端点的距离,并且 的时候,答案不为 ,其余情况都为 。
考虑找出一条直径,枚举转折点 ,找出 子树内 最大的两个 ,则直径为 ,树形 即可。
- 直径唯一
则此类情况下,答案为 当且仅当有一个点 到直径两端距离相等。
接下来考虑 的情况。
依然考虑以上算法,考虑一次操作后树为两个点分别挂着一堆点(分别将两个点集记为 )。令 中与 相连边最长的点为 , 同理。考虑由于 的边权为此时树中的最大边权,此时树的直径为 ,则 中的点经历这次操作都会连到 ,且边权为原来的加上 , 中的点同理。新树中 等于旧树中 。
考虑边权的增长是指数级的,我们不能直接维护,必须在模 意义下维护相对顺序。考虑拿两个队列 维护 中 的相对顺序。考虑一次操作后先得出 并进行排序。考虑一次操作,我们会取出两边的最大值并将其置为零,将其余的整体加上一个正数,并将 加入队列。考虑维护整体加的 ,由于 原先是最小值 ,我们将 加入最后即可。
再考虑判 :
- 直径不唯一
即 或 中前两大值相等。不能直接判断,因为我们的边权是在模 下考虑的。但是我们考虑一次操作后只会取 次,我们只需要在一次操作后的 和 中判断 中是否 即可,需要注意,当 与 有一个空的时候不应判断这种情况,因为此时直径不唯一但有同一端点。
或者你加个哈希也可以。
这个情况是答案必然为 的。
- 直径唯一
不可能有任何一个点到新直径两端距离相等,因为旧直径是旧的树中边权最大的。
综上,我们做到了 的复杂度。瓶颈是排序。
代码细节比较多,就贴上来了:
#include<bits/stdc++.h> using namespace std; #define ll long long #define ui unsigned namespace IO{//by cyffff } const int N=1e6+10; int n,k,head[N],cnt; struct Edge{ int to,nxt,w; }a[N<<1]; inline void add(int u,int v,int w){ cnt++; a[cnt].to=v; a[cnt].nxt=head[u]; a[cnt].w=w; head[u]=cnt; } int s,t; ll mx,dep[N][2],dis; inline void dfs(int x,int fa,int tp){ if(!fa) dep[x][tp]=0; for(int i=head[x];i;i=a[i].nxt){ int t=a[i].to; if(t==fa) continue; dep[t][tp]=dep[x][tp]+a[i].w; dfs(t,x,tp); } } vector<ll>s1,s2; queue<ui>d1,d2; ui tag1,tag2,di,ans; /* 7 1 1 1 2 2 1 4 2 4 4 4 5 4 */ int main(){ // freopen("16.in","r",stdin); n=read(),k=read(); for(int i=2;i<=n;i++){ int f=i-read(),w=read(); add(f,i,w),add(i,f,w); } dfs(1,0,0); for(int i=1;i<=n;i++) if(dep[i][0]>mx) mx=dep[i][0],s=i; dfs(s,0,0); for(int i=1;i<=n;i++) if(dep[i][0]>dis) dis=dep[i][0],t=i; dfs(t,0,1); bool fl1=0,fl2=0; for(int i=1;i<=n;i++){ if(i==s||i==t) continue; if(dep[i][0]==dep[t][0]) fl1=1; if(dep[i][1]==dep[s][1]) fl2=1; } if(fl1&&fl2) return puts("-1"),0; if((fl1||fl2)&&k>1) return puts("-1"),0; if(fl1){ for(int i=1;i<=n;i++){ if(i==s||i==t) continue; if(dep[i][0]<dep[i][1]) return puts("-1"),0; } } if(fl2){ for(int i=1;i<=n;i++){ if(i==s||i==t) continue; if(dep[i][0]>dep[i][1]) return puts("-1"),0; } } for(int i=1;i<=n;i++){ if(i==s||i==t) continue; if(dep[i][0]==dep[i][1]) return puts("-1"),0; if(dep[i][0]>dep[i][1]) s1.push_back(dep[i][0]); else s2.push_back(dep[i][1]); } sort(s1.begin(),s1.end(),greater<ll>()); sort(s2.begin(),s2.end(),greater<ll>()); if(s1.size()&&s2.size()){ for(int i=0;i+1<s1.size()&&i<min(n,k-1);i++) if(s1[i]==s1[i+1]) return puts("-1"),0; for(int i=0;i+1<s2.size()&&i<min(n,k-1);i++) if(s2[i]==s2[i+1]) return puts("-1"),0; } for(auto x:s1) d1.push((ui)x);//,printf("%d ",x);puts(""); for(auto x:s2) d2.push((ui)x);//,printf("%d ",x);puts(""); di=dis; for(int i=2;i<=k;i++){ ui x=0,y=0; bool fl1=0,fl2=0; if(!d1.empty()) x=d1.front()+tag1,d1.pop(),fl1=1; if(!d2.empty()) y=d2.front()+tag2,d2.pop(),fl2=1; if(fl1) d1.push(-tag1),tag1+=y+di; if(fl2) d2.push(-tag2),tag2+=x+di; di+=x+y; // printf("%d %d %d %d %d\n",x,y,tag1,tag2,di); } while(!d1.empty()) ans+=d1.front()+tag1,d1.pop(); while(!d2.empty()) ans+=d2.front()+tag2,d2.pop(); ans+=di; write(ans); flush(); }
- 1
信息
- ID
- 8201
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者