题目

题目大意

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


正解

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

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

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

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

设直径端点为\(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. sql developer 中文乱码解决办法

    近日在fedora13中安装了oracle和sql developer,在英文环境下启动sql developer正常,可是切换到中文环境下就显示乱码.google了一下,确定是因为JDK不支持中文的 ...

  2. 当input中的内容改变时触发的事件

    当input中的内容改变时触发的事件 1 $('#id').bind('input propertychange', function() { //处理内容 } 循环js事件 $(document). ...

  3. enumerate()(Python)

    >>> E=enumerate('spam') >>> E <enumerate object at 0x1021ceca8> >>> ...

  4. JavaScript面向对象小抄集

    前言 本文旨在记录JavaScript中面向对象的基础知识 搞明白JavaScript中的面向对象 一切都是对象 JavaScript中,除了基本类型外,其它类型都是对象类型 所谓对象就是若干属性的集 ...

  5. PHP curl_reset函数

    curl_reset— 重置libcurl会话句柄的所有选项. 说明 void curl_reset ( resource $ch ) 该函数将重新初始化cURL的所有选项值(默认值). 注意:cur ...

  6. 【Flutter学习】可滚动组件之滚动监听及控制

    一,概述 ScrollController可以用来控制可滚动widget的滚动位置 二,ScrollController 构造函数 ScrollController({ double initialS ...

  7. Python基础教程(001)--前言

    前言 人生苦短,我用Python. Python的作者,Guido von Rossum,确实是荷兰人.1982年,Guido从阿姆斯特丹大学(University of Amsterdam)获得了数 ...

  8. JMeter 阶梯式加压测试插件 Stepping Thread Group

    在日常性能测试过程中,有时需要对被测对象不断的增加压力,直至达到某个值后,并持续运行一段时间.这里将借助jmeter插件模拟这种情况. 本文介绍在jmeter中,使用插件Stepping Thread ...

  9. 2018icpc沈阳/gym101955 J How Much Memory Your Code Is Using? 签到

    题意: 给你定义一堆变量,计算一下这些变量共占了多少k内存. 题解: 按题意模拟即可,善用ceil() // // Created by melon on 2019/10/22. // #includ ...

  10. ROS录制主题和放

    1.按照指定名称录制指定主题, 如录制主题为line_markers,名称为line_extraction的bag包. rosbag record -O line_extraction.bag /li ...