zoukankan      html  css  js  c++  java
  • 洛谷 P4408 [NOI2003]逃学的小孩

    来看题板,这个题大体意思就是,在一个图上,选ABC三个点,然后连接起来,而且在min(AC,BC)+AB的情况下,路径最长是多少?

    经过一波缜密的斯烤,我们可以得出一个显而易见的结论“AB一定是树的直径!(啊一点也不显而易见……当时为合理性纠结了好久……)

    啊因为没有任何解释,大家不免会对合理性产生怀疑,然后接下来,我们就用反证法证明一下吧(顺着想真的想不出来,突然想起数学老师说过,如果想证明一个方法是错的,就举一个和他不符的例子)。先假设一个情况,设AB是树的直径,但最长是CE+DE,先假设DE和AB之间有交点,想象图:

    树的直径是一棵树里最长的路径,根据树的直径的定义,我们可以推算出一些公式:

    1:a+b>d+f+e

    2:b+a>=b+e

    3:b+a>=b+d+f

    4:a+b>=a+e

    5:a+b>=a+d+f

    整理一下公式,我们可以得到3个已知事项:

    [1]:a+b>d+f+e

    [2]:a>=e&&a>=d+f

    [3]:b>=e&&b>=d+f

    然后,我们假设cd1是三点分别在A,B,C时的最长路径

    假设cd2是三点分别在D,E,C时的最长路径。

    由上可得:

    cd1=a+b+min(c+f+a,c+f+b)=a+b+c+f+min(a,b);

    cd2=d+e+f+min(c+d,c+f+e)=c+d+e+f+min(d,f+e);

    cd1-cd2=a+b+c+f+min(a,b)-c-d-e-f-min(d,f+e)=(a+b-d-e-f)+f+(c-c)+min(a,b)-min(d,f+e)=(a+b-d-e-f)+(c-c)+min(a,b)-min(d-f,e);

    出现分歧了!

    现在有两种可能性

    1:当d-f<=e时

    cd1-cd2就等于(a+b-d-e-f)+min(a,b)-(d-f);

    根据上面的[2],我们可以得到a>=d+f>=d-f

    根据上面的[3],我们可以得到b>=d+f>=d-f

    根据上面的[1],我们可以得到a+b>d-e-f

    cd1-cd2最终会变成:一个大于0的数+0+一个大于等于0的数

    所以,在d-f<=e的情况下假设不成立。

    2:当d-f>e时

    cd1-cd2就等于(a+b-d-e-f)+min(a,b)-e;

    根据上面的[2],我们可以得到a>=e

    根据上面的[3],我们可以得到b>=e

    根据上面的[1],我们可以得到a+b>d-e-f

    cd1-cd2最终会变成:一个大于0的数+0+一个大于等于0的数

    所以,在d-f>e的情况下假设也不成立。

    综上所述,假设不成立!

    然鹅还有一种情况……

    第二种情况,和之前设的一样,只是这次AB和DE不相交

    想象图2:

    其实和上一个没什么区别(小声bb

    由树的直径的性质,我们可以得到一些公式:

    1:a+b>d+f+e

    2:b+a>=b+e+g

    3:b+a>=b+d+f+g

    4:a+b>=a+e+g

    5:a+b>=a+d+f+g

    经过转化:

    [1]:a+b>d+f+e

    [2]:a>=e+g&&a>=d+f+g

    [3]:b>=e+g&&b>=d+f+g

    然后,我们假设cd1是三点分别在A,B,C时的最长路径

    假设cd2是三点分别在D,E,C时的最长路径。

    由上可得:

    cd1=a+b+min(c+f+a+g,c+f+b+g)=a+b+c+f+g+min(a,b);

    cd2=d+e+f+min(c+d,c+f+e)=c+d+e+f+min(d,f+e);

    cd1-cd2=(a+b-d-e-f)+(c-c)+min(a,b)-min(d-f-g,e-g);(具体变换过程请参考第一种假设)

    又是分歧!

    1:在d-f-g<=e-g的时候

    cd1-cd2就等于(a+b-d-e-f)+(c-c)+min(a,b)-(d-f-g);

    根据上面的[2],我们可以得到a>=d+f+g>=d-f-g

    根据上面的[3],我们可以得到b>=d+f+g>=d-f-g

    根据上面的[1],我们可以得到a+b>d-e-f

    cd1-cd2最终会变成:一个大于0的数+0+一个大于等于0的数

    所以,在d-f-g<=e-g的情况下假设还不成立。

    2:在e-g<d-f-g的时候

    cd1-cd2就等于(a+b-d-e-f)+(c-c)+min(a,b)-(e-g);

    根据上面的[2],我们可以得到a>=e+g>=e-g

    根据上面的[3],我们可以得到b>=e+g>=e-g

    根据上面的[1],我们可以得到a+b>d-e-f

    cd1-cd2最终还是会变成:一个大于0的数+0+一个大于等于0的数

    所以,在e-g<d-f-g的情况下假设依然不成立。

    综上所属,AB一定是树的直径。

    树的直径都会求吧……,不会的话我下一篇博客再写,这一篇实在有点太长了(对我来说

    求出树的直径之后,只需要遍历一遍,枚举C点就好了。

    好了上代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    using namespace std;
    long long n,m,a,b,c;
    long long head[200005],ans;
    long long zc[200005],zd,bh;
    long long bhA,bhB,bhAcd[200005],bhBcd[200005];
    long long zshu;
    struct hehe
    {
    	long long w,cd,syg;
    }lsqxx[500005];
    void add(long long t,long long w,long long cd)//链式前向星,相信大家都很熟悉 
    {
    	ans++;
    	lsqxx[ans].w=w;
    	lsqxx[ans].cd=cd;
    	lsqxx[ans].syg=head[t];
    	head[t]=ans;
    }
    void djstl(long long wz,long long bb)
    {
    	for(long long i=head[wz];i!=0;i=lsqxx[i].syg)//迪杰斯特拉求最长路,非常好用 
    	{
    		if(lsqxx[i].w==bb)
    		{
    			continue;
    		}
    		if(zc[lsqxx[i].w]<zc[wz]+lsqxx[i].cd)//松弛操作 
    		{
    			zc[lsqxx[i].w]=zc[wz]+lsqxx[i].cd;
    			if(zc[lsqxx[i].w]>=zd)//更新最大距离 
    			{
    				zd=zc[lsqxx[i].w];
    				bh=lsqxx[i].w;//更新最远距离编号 
    			}
    			djstl(lsqxx[i].w,wz);//继续查找 
    		}
    	}
    }
    int main()
    {
    	scanf("%lld%lld",&n,&m);
    	for(long long i=0;i<m;i++)
    	{
    		scanf("%lld%lld%lld",&a,&b,&c);
    		add(a,b,c);//双向存图 
    		add(b,a,c);
    	}
    	djstl(1,0);//随便选一个点开始遍历 
    	bhA=bh;//bh就是离他最远的一个点,这个点一定是直径的一个端点。存下来 
    	zd=-999999;//初始化 
    	memset(zc,0,sizeof(zc));
    	djstl(bhA,0);//继续找另一个端点 
    	for(long long i=1;i<=n;i++)
    	{
    		bhAcd[i]=zc[i];//记录一下每个点到自己的最远距离 
    	}
    	bhB=bh;//同上 
    	zd=-999999;
    	memset(zc,0,sizeof(zc));
    	djstl(bhB,0);
    	for(long long i=1;i<=n;i++)
    	{
    		bhBcd[i]=zc[i];
    	}
    	for(long long i=1;i<=n;i++)//枚举C点 
    	{
    		zshu=max(zshu,min(bhBcd[i],bhAcd[i]));
    	}
    	printf("%lld
    ",zshu+bhAcd[bhB]);//输出 
    	return 0;
    }
    

    啊这可能是我写过的最长的一篇博客了……

    结果最后都没讲求树的直径的原理……,啊算了下次再讲吧……

  • 相关阅读:
    App测试从入门到精通之安装、卸载和运行测试
    App测试从入门到精通之App分类和场景操作系统
    一步到位带你入门Selenium
    MAMP和WAMP搭建Web环境,数据库,数据分布可视化
    爬虫 Http请求,urllib2获取数据,第三方库requests获取数据,BeautifulSoup处理数据,使用Chrome浏览器开发者工具显示检查网页源代码,json模块的dumps,loads,dump,load方法介绍
    Python 基本语法,文件读写,数据结构和类型
    python 数据工程 and 开发工具Sublime
    jieba user guide
    python各类项目模块记录
    python parse xml using DOM
  • 原文地址:https://www.cnblogs.com/lichangjian/p/13446680.html
Copyright © 2011-2022 走看看