题目

题目大意

给你一棵树,对于每一条边,求删去这条边之后,再用一条边(自己定)连接两个连通块,形成的树的直径最小是多少。


正解

首先,将这棵树的直径给找出来。显然,如果删去的边不在直径上,那么答案就是直径。

接下来考虑删去的边在直径上的情况。

自己连的边应该要是两棵树的直径的中点(中点就是直径上到端点最大距离最小的点)。

答案就是两棵树的直径的一半(当然这是粗略的说法)加上边权,和两棵树内部的直径长度的最大值。

设直径端点为\(S\)和\(T\),现在想象直径是横过来的一条线,有一堆树挂在上面。

在直径上从左到右枚举删去哪条边,顺带着维护中点在哪里。

有个结论:中点肯定在原来的直径上。

(后面都以\(S\)的一边为例,显然另一边是一样的)

反证法,设中点为\(x\),\(x\)不在直径上。设\(y\)为\(x\)到\(S\)路径上第一个出现在直径上的点。

现在找最远的点\(z\)。

如果\(z\)在\(y\)子树之外,那么路径就是\(x\)到\(y\)和\(y\)到\(z\)的距离。这时候如果要使\(y\)到\(z\)最大,则\(z=S\)。这时候将\(x\)变成\(y\)更优。

如果\(z\)在\(x\)子树之内,那么\(x\)到\(z\)的距离比\(x\)到\(S\)的距离长,与假设矛盾。

如果\(z\)在\(y\)子树之内,在\(x\)子树之外,那么\(y\)到\(z\)的距离比\(y\)到\(S\)的距离长,矛盾。

接下来考虑如何维护直径。

在原来的直径上,对于每个节点,预处理出\(f_x\)表示\(x\)子树中最远点到\(x\)的长度。

设\(disS_x\)为\(x\)到\(S\)的距离。

显然,新的直径的一个端点是\(S\)。直径可以分成在原来直径上和在某棵子树内的两段。

设\(x\)为直径的拐点,则直径的长度为\(disS_x+f_x\)

设\(a\)为直径的中点,则直径一半的长度(形象的说法)为\(max(disS_a,disS_x+f_x-disS_a)\)

现在被删去的边在原来的直径上从左往右移动,每个拐点都能搞出一条路径。在这些路径中找长度最大的,作为直径,然后\(a\)移动到\(max(disS_a,disS_x+f_x-disS_a)\)最小的地方,这时候\(a\)就求出来了。

在这个过程中,我们发现\(a\)只会从\(S\)向\(T\)移动。

所以直接\(O(n)\)做就可以了(题解说要单调队列,但实际上完全不用。具体见代码。)


代码

using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2000010
#define ll long long
int n;
struct EDGE{
int to,w,num;
EDGE *las;
} e[N*2],*last[N];
int ne;
unsigned long long num;
unsigned long long get(){
num^=(num<<13);
num^=(num>>17);
num^=(num<<5);
return num;
}
void gen(){
int B,D;
scanf("%d%llu%d%d",&n,&num,&B,&D);
for(int i=2;i<=n;i++){
int a=get()%min(i-1,B)+i-min(i-1,B),b=get()%D;
e[ne]={a,b,i-1,last[i]};
last[i]=e+ne++;
e[ne]={i,b,i-1,last[a]};
last[a]=e+ne++;
}
}
ll ans[N];
int q[N];
ll ds[N],dt[N],alen;
int S,T,pre[N],suc[N];
EDGE *et[N];
void init(){
static int vis[N];
int BZ,h,t;
vis[1]=BZ=1;
q[h=t=1]=1;
ll *dis=ds;
dis[1]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (vis[y]!=BZ){
vis[y]=BZ;
dis[y]=dis[x]+ei->w;
q[++t]=y;
}
}
}
S=1;
for (int i=1;i<=n;++i)
if (dis[i]>dis[S])
S=i;
q[h=t=1]=S;
vis[S]=++BZ;
dis[S]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (vis[y]!=BZ){
vis[y]=BZ;
dis[y]=dis[x]+ei->w;
pre[y]=x;
et[y]=ei;
q[++t]=y;
}
}
}
T=S;
for (int i=1;i<=n;++i)
if (dis[i]>dis[T])
T=i;
for (int i=T;i!=S;i=pre[i])
suc[pre[i]]=i;
suc[T]=0;
for (int i=1;i<=n;++i)
if (!suc[i] && i!=T)
pre[i]=0;
}
ll f[N];
int fa[N];
void dp1(int rt){
int h,t;
q[h=t=1]=rt;
fa[rt]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=pre[rt] && y!=suc[rt] && y!=fa[x]){
ans[ei->num]=alen;
fa[y]=x;
q[++t]=y;
}
}
}
for (int i=t;i>=1;--i){
int x=q[i],y;
f[x]=0;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=pre[rt] && y!=suc[rt] && y!=fa[x])
f[x]=max(f[x],f[y]+ei->w);
}
}
}
ll gs[N],gt[N];
void dp2(int rt,int *cant,ll *g){
int h,t;
q[h=t=1]=rt;
fa[rt]=0;
while (h<=t){
int x=q[h++],y;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=cant[x] && y!=fa[x]){
fa[y]=x;
q[++t]=y;
}
}
}
for (int i=t;i>=1;--i){
int x=q[i],y;
ll fmx=0,smx=0;
f[x]=0;
g[x]=0;
for (EDGE *ei=last[x];ei;ei=ei->las){
y=ei->to;
if (y!=cant[x] && y!=fa[x]){
g[x]=max(g[x],g[y]);
if (f[y]+ei->w>fmx)
smx=fmx,fmx=f[y]+ei->w;
else if (f[y]+ei->w>smx)
smx=f[y]+ei->w;
}
}
f[x]=fmx;
g[x]=max(g[x],fmx+smx);
}
}
ll hs[N],ht[N];
void calc(int beg,int end,int *nxt,ll *h,ll *dis){
int a=beg,mx=beg;
h[beg]=f[beg];
for (int x=nxt[beg];x!=end;x=nxt[x]){
if (dis[x]+f[x]>dis[mx]+f[mx]){
mx=x;
while (a!=x && max(dis[nxt[a]],dis[mx]+f[mx]-dis[nxt[a]])<max(dis[a],dis[mx]+f[mx]-dis[a]))
a=nxt[a];
}
h[x]=max(dis[a],dis[mx]+f[mx]-dis[a]);
}
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
gen();
init();
alen=ds[T];
dp2(T,suc,gs),dp2(S,pre,gt);
for (int i=S;i;i=suc[i])
dt[i]=alen-ds[i],dp1(i);
calc(S,T,suc,hs,ds);
calc(T,S,pre,ht,dt);
for (int i=S;i!=T;i=suc[i])
ans[et[suc[i]]->num]=max(max(gs[i],gt[suc[i]]),hs[i]+ht[suc[i]]+et[suc[i]]->w);
ll s=0;
for (int i=1;i<n;++i)
s^=ans[i]%998244353*i%998244353;
printf("%lld\n",s);
return 0;
}

总结

论猜结论的重要性……

6380. 【NOIP2019模拟2019.10.06】小w与最长路(path)的更多相关文章

  1. 6389. 【NOIP2019模拟2019.10.26】小w学图论

    题目描述 题解 之前做过一次 假设图建好了,设g[i]表示i->j(i<j)的个数 那么ans=∏(n-g[i]),因为连出去的必定会构成一个完全图,颜色互不相同 从n~1染色,点i的方案 ...

  2. 【NOIP2019模拟2019.10.07】果实摘取 (约瑟夫环、Mobius反演、类欧、Stern-Brocot Tree)

    Description: 小 D 的家门口有一片果树林,果树上果实成熟了,小 D 想要摘下它们. 为了便于描述问题,我们假设小 D 的家在二维平面上的 (0, 0) 点,所有坐标范围的绝对值不超过 N ...

  3. 6392. 【NOIP2019模拟2019.10.26】僵尸

    题目描述 题解 吼题但题解怎么这么迷 考虑一种和题解不同的做法(理解) 先把僵尸离散化,h相同的钦(ying)点一个大小 (可以发现这样每种情况只会被算正好一次) 计算完全被占领的方案,然后1-方案/ ...

  4. 6377. 【NOIP2019模拟2019.10.05】幽曲[埋骨于弘川]

    题目描述 题解 随便bb 详细题解见 https://www.cnblogs.com/coldchair/p/11624979.html https://blog.csdn.net/alan_cty/ ...

  5. 6383. 【NOIP2019模拟2019.10.07】果实摘取

    题目 题目大意 给你一个由整点组成的矩形,坐标绝对值范围小于等于\(n\),你在\((0,0)\),一开始面向\((1,0)\),每次转到后面第\(k\)个你能看到的点,然后将这条线上的点全部标记删除 ...

  6. 6374. 【NOIP2019模拟2019.10.04】结界[生与死的境界]

    题目 题目大意 给你一个数列,每次可以选择任意两个相邻的数\(x\)和\(y\),将其删去,并在原来位置插入\(x+2y\). 每次询问一个区间,对这个区间进行上述操作.求最后剩下的数最大是多少. 答 ...

  7. 【NOIP2017模拟6.25】小W的动漫

    题目 小W最近迷上了日本动漫,每天都有无数部动漫的更新等着他去看,所以他必须将所有的动漫排个顺序,当然,虽然有无数部动漫,但除了1号动漫,每部动漫都有且仅有一部动漫是它的前传(父亲),也就是说,所有的 ...

  8. 洛谷P5284 [十二省联考2019]字符串问题(SAM+倍增+最长路)

    题面 传送门 题解 首先,我们把串反过来,那么前缀就变成后缀,建一个\(SAM\).我们发现一个节点的后缀是它的所有祖先 那么我们是不是直接按着\(parent\)树建边就可以了呢? 显然不是.我们假 ...

  9. [JZOJ6359] 【NOIP2019模拟2019.9.15】小ω的树

    题目 题目大意 给你一棵树,带点权和边权. 要你选择一个联通子图,使得点权和乘最小边权最大. 支持修改点权操作. 思考历程 显然,最先想到的当然是重构树了-- 重构树就是在做最大生成树的时候,当两个联 ...

随机推荐

  1. 【leetcode】931. Minimum Falling Path Sum

    题目如下: Given a square array of integers A, we want the minimum sum of a falling path through A. A fal ...

  2. python学习--第二天 爬取王者荣耀英雄皮肤

    今天目的是爬取所有英雄皮肤 在爬取所有之前,先完成一张皮肤的爬取 打开anacond调出编译器Jupyter Notebook 打开王者荣耀官网 下拉找到位于网页右边的英雄/皮肤 点击[+更多] 进入 ...

  3. 原生js和jquery

    $(document).ready(function() {//jquery的写法 }); (function () { //原生js的写法 })();

  4. POJ 3159 Candies(spfa、差分约束)

    Description During the kindergarten days, flymouse was the monitor of his class. Occasionally the he ...

  5. sql server 基础语法2

    别名,选择,查询,排序,去重,筛选 select * from UserInfo as ui --起别名 select UserName,UserPwd --指定选择的列 from UserInfo ...

  6. Monkeyrunner学习

    可以写一个pyhon工程,安装在android进行测试,还可以截屏操作.Monkeyrunner为framework层开发.MonkeyRunner本身是Java做的,为了和Python连接,做了一个 ...

  7. 2018-2019-2 20175223 实验三《敏捷开发与XP实践》实验报告

    目录 北京电子科技学院(BESTI)实验报告 实验名称:实验三 敏捷开发与XP实践 实验内容.步骤与体会: 一.实验三 敏捷开发与XP实践-1 二.实验三 敏捷开发与XP实践-2 三.实验三 敏捷开发 ...

  8. JAVA 的StringBuffer类

    StringBuffer类和String一样,也用来代表字符串,只是由于StringBuffer的内部实现方式和String不同,所以StringBuffer在进行字符串处理时,不生成新的对象,在内存 ...

  9. Django基础篇(二)与mysql配合使用

    需求:模拟实现学员管理系统.<*_* 从基础做起> 表结构如下: 班级/学生/老师 班级表: id     title 1       xx 2 xx 学生表: id    name    ...

  10. PHP使用引用变量foreach时,切记其他循环不要使用同一个名字的变量

    foreach ($log['data'] as $k => &$value) { if ($value['token'] != 0) { $value['change_num'] = ...