NOIP2007提高组

2020-1-18

/* 期末考试成绩差点掉出年级前30 我差点直接崩掉 却还要在这儿写博客*/


第一题 

统计数字

题目描述

某次科研调查时得到了nnn个自然数,每个数均不超过1500000000(1.5×109)1500000000(1.5 \times 10^9)1500000000(1.5×109)。已知不相同的数不超过100001000010000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出统计结果。

输入格式

n+1n+1n+1行。

第一行是整数nnn,表示自然数的个数;

222至n+1n+1n+1每行一个自然数。

输出格式

mmm行(mmm为nnn个自然数中不相同数的个数),按照自然数从小到大的顺序输出。

每行输出222个整数,分别是自然数和该数出现的次数,其间用一个空格隔开。

输入输出样例

输入 #1
8
2
4
2
4
5
100
2
100
输出 #1
2 3
4 2
5 1
100 2

说明/提示

40%40\%40%的数据满足:1≤n≤10001 \le n \le 10001n1000

80%80\%80%的数据满足:1≤n≤500001 \le n \le 500001n50000

100%100\%100%的数据满足:1≤n≤2000001 \le n \le 2000001n200000,每个数均不超过1500000000(1.5×109)1500 000 000(1.5 \times 109)1500000000(1.5×109)

NOIP 2007 提高第一题

 一些经验(教训): 第一题又特么打崩了?!

好吧 其实是我手贱写了两种算法 把STL那种没学过不知道问题的交上去了......

错误CODE

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+100;
long long read(){
    long long ans=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)) f*=(ch=='-')?-1:1,ch=getchar();
    do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar();
    while(isdigit(ch));
    return ans*f; 
}
map<long long,int>h;
int main(){
    //freopen("count.in","r",stdin);
    //freopen("count.out","w",stdout);
    long long n=read();
    for(int i=1;i<=n;i++){
        long long x=read();
        h[x]++;
    }
    for(int i=1;i<=h.size();i++){
        if(h[i])cout<<i<<" "<<h[i]<<endl;
    }
    
    return 0;
}

当我发现这道题可以直接暴力模拟AC时 .......

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
    int t=0; char ch=get;
    while(ch<'0' || ch>'9') ch=get;
    while(ch<='9' && ch>='0') t=t*10+ch-'0',ch=get;
    return t;
} 
struct num{
    int id,sum;
}a[200010]; //每个数出现的次数及它的值
int tot,n,h[200010];
int main()
{
    n=read(),a[0].id=-199; 
    for(re int i=1;i<=n;i++)
        h[i]=read();  
    sort( h+1,h+n+1); 
    for(re int i=1;i<=n;i++)
    {
        if(a[tot].id==h[i])a[tot].sum++; 
        else a[++tot].id=h[i],a[tot].sum=1; 
    }
    for(re int i=1;i<=tot;i++)
        cout<<a[i].id<<' '<<a[i].sum<<endl; 
    return 0;
}

第二题

字符串的展开

题目描述

在初赛普及组的“阅读程序写结果”的问题中,我们曾给出一个字符串展开的例子:如果在输入的字符串中,含有类似于“d-h”或者“4-8”的字串,我们就把它当作一种简写,输出时,用连续递增的字母或数字串替代其中的减号,即,将上面两个子串分别输出为“defgh”和“45678"。在本题中,我们通过增加一些参数的设置,使字符串的展开更为灵活。具体约定如下:

(1) 遇到下面的情况需要做字符串的展开:在输入的字符串中,出现了减号“-”,减号两侧同为小写字母或同为数字,且按照ASCII码的顺序,减号右边的字符严格大于左边的字符。

(2) 参数p1p_1p1:展开方式。p1=1p_1=1p1=1时,对于字母子串,填充小写字母;p1=2p_1=2p1=2时,对于字母子串,填充大写字母。这两种情况下数字子串的填充方式相同。p1=3p_1=3p1=3时,不论是字母子串还是数字字串,都用与要填充的字母个数相同的星号“*”来填充。

(3) 参数p2p_2p2:填充字符的重复个数。p2=kp_2=kp2=k表示同一个字符要连续填充k个。例如,当p2=3p_2=3p2=3时,子串“d-h”应扩展为“deeefffgggh”。减号两边的字符不变。

(4) 参数p3p_3p3:是否改为逆序:p3=1p3=1p3=1表示维持原来顺序,p3=2p_3=2p3=2表示采用逆序输出,注意这时候仍然不包括减号两端的字符。例如当p1=1p_1=1p1=1、p2=2p_2=2p2=2、p3=2p_3=2p3=2时,子串“d-h”应扩展为“dggffeeh”。

(5) 如果减号右边的字符恰好是左边字符的后继,只删除中间的减号,例如:“d-e”应输出为“de”,“3-4”应输出为“34”。如果减号右边的字符按照ASCII码的顺序小于或等于左边字符,输出时,要保留中间的减号,例如:“d-d”应输出为“d-d”,“3-1”应输出为“3-1”。

输入格式

共两行。

111行为用空格隔开的333个正整数,依次表示参数p1,p2,p3p_1,p_2,p_3p1,p2,p3

222行为一行字符串,仅由数字、小写字母和减号“−-−”组成。行首和行末均无空格。

输出格式

共一行,为展开后的字符串。

输入输出样例

输入 #1
1 2 1
abcs-w1234-9s-4zz
输出 #1
abcsttuuvvw1234556677889s-4zz
输入 #2
2 3 2
a-d-d
输出 #2
aCCCBBBd-d

说明/提示

40%40\%40%的数据满足:字符串长度不超过555

100%100\%100%的数据满足:1≤p1≤3,1≤p2≤8,1≤p3≤21 \le p_1 \le 3,1 \le p_2 \le 8,1 \le p_3 \le 21p13,1p28,1p32。字符串长度不超过100100100

NOIP 2007 提高第二题

考场:思路简单 照着题目要求if模拟但是代码冗杂并且不好揪错

但是经过我不懈的努力还是用3KB的量写出来啦! (我在嚣张什么)

#include<bits/stdc++.h>
using namespace std;
int p1,p2,p3,len,idx;
int w[105];
char a[105];
void zhan(){
        for(int i=1; i<=idx; i++) {
        if(i==idx&&w[i]==w[i-1]+1)
        break;
        if(w[i]+1==w[i+1]){
            //cout<<endl;
            //cout<<w[i-1]+2<<"->"<<w[i+1]+1<<endl;
            for(int j=w[i-1]+2;j<=w[i+1]+1;j++)
            cout<<a[j];
            continue;
        }
        if(int(a[w[i]-1])>=int(a[w[i]+1])||(int(a[w[i]-1])>=48&&int(a[w[i]-1])<=57&&int(a[w[i]+1])>57)) {
            for(int j=w[i-1]+2; j<=w[i]+1; j++)
                cout<<a[j];
            continue;
        }
        if(int(a[w[i]-1])+1==int(a[w[i]+1])) {
            for(int j=w[i-1]+2; j<=w[i]+1; j++) {
                if(a[j]!='-')
                    cout<<a[j];
            }
            continue;
        }
        if(int(a[w[i]-1])>=48&&int(a[w[i]-1])<=57) {
            if(p1==3) {
                for(int j=w[i-1]+2; j<=w[i]-1; j++)
                    cout<<a[j];
                //cout<<a[w[i]-1];
                for(int j=a[w[i]-1]-'0'+1; j<=a[w[i]+1]-'0'-1; j++) {
                    for(int k=1; k<=p2; k++)
                        cout<<'*';
                }
                cout<<a[w[i]+1];
            } else {
                if(p3==1) {
                    for(int j=w[i-1]+2; j<=w[i]-1; j++)
                        cout<<a[j];
                    //cout<<a[w[i]-1];
                    for(int j=a[w[i]-1]-'0'+1; j<=a[w[i]+1]-'0'-1; j++) {
                        for(int k=1; k<=p2; k++)
                            cout<<j;
                    }
                    cout<<a[w[i]+1];
                }
                if(p3==2) {
                    for(int j=w[i-1]+2; j<=w[i]-1; j++)
                        cout<<a[j];
                    //cout<<a[w[i]-1];
                    for(int j=a[w[i]+1]-'0'-1; j>=a[w[i]-1]-'0'+1; j--) {
                        for(int k=1; k<=p2; k++)
                            cout<<j;
                    }
                    cout<<a[w[i]+1];
                }
            }
        }
        if(int(a[w[i]-1])>=97&&int(a[w[i]-1])<=122) {
            if(p1==3) {
                for(int j=w[i-1]+2; j<=w[i]-1; j++)
                    cout<<a[j];
                //cout<<a[w[i]-1];
                for(int j=int(a[w[i]-1])+1; j<=int(a[w[i]+1])-1; j++) {
                    for(int k=1; k<=p2; k++)
                        cout<<'*';
                }
                cout<<a[w[i]+1];
            } else {
                if(p1==1) {
                    if(p3==1) {
                        for(int j=w[i-1]+2; j<=w[i]-1; j++)
                            cout<<a[j];
                        //cout<<a[w[i]-1];
                        for(int j=int(a[w[i]-1])+1; j<=int(a[w[i]+1])-1; j++) {
                            for(int k=1; k<=p2; k++)
                                cout<<char(j);
                        }
                        cout<<a[w[i]+1];
                    }
                    if(p3==2) {
                        for(int j=w[i-1]+2; j<=w[i]-1; j++)
                            cout<<a[j];
                        //cout<<a[w[i]-1];
                        for(int j=int(a[w[i]+1])-1; j>=int(a[w[i]-1])+1; j--) {
                            for(int k=1; k<=p2; k++)
                                cout<<char(j);
                        }
                        cout<<a[w[i]+1];
                    }
                }
                if(p1==2) {
                    if(p3==1) {
                        for(int j=w[i-1]+2; j<=w[i]-1; j++)
                            cout<<a[j];
                        //cout<<a[w[i]-1];
                        for(int j=int(a[w[i]-1])+1; j<=int(a[w[i]+1])-1; j++) {
                            for(int k=1; k<=p2; k++)
                                cout<<char(j-32);
                        }
                        cout<<a[w[i]+1];
                    }
                    if(p3==2) {
                        for(int j=w[i-1]+2; j<=w[i]-1; j++)
                            cout<<a[j];
                        //cout<<a[w[i]-1];
                        for(int j=int(a[w[i]+1])-1; j>=int(a[w[i]-1])+1; j--) {
                            for(int k=1; k<=p2; k++)
                                cout<<char(j-32);
                        }
                        cout<<a[w[i]+1];
                    }

                }
            }
            continue;
        }
    }
    for(int i=w[idx]+2;i<len;i++)
    cout<<a[i];
    
}
int main() {
    //freopen("expand.in","r",stdin);
    //freopen("expand.out","w",stdout);
    cin>>p1>>p2>>p3;
    cin>>a;
    len=strlen(a);
    //cout<<len<<endl;
    //cout<<len;
    for(int i=0; i<len; i++) {
        //cout<<a[i];
        if(a[i]=='-'&&a[i-1]=='-'&&i-1==0)
        continue;
        if(a[i]=='-'&&i!=0&&i!=len-1)
            w[++idx]=i;
    }
    w[0]=-2;
    //for(int i=1; i<=idx; i++)
    //    cout<<w[i]<<" ";
    //    cout<<endl;
    zhan();
    return 0;
}

下来后 和神犇PHDHD交流后发现人家代码长度后......

思路:函数调用判断减少for循环啊 if判断的数量(哎我真的蠢)

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

long long read(){
    long long ans = 0, f = 1;
    char ch = getchar();
    while(!isdigit(ch))
        f *= (ch == '-') ? -1 : 1, ch = getchar();
    do ans = (ans << 1) + (ans << 3) + (ch ^ 48), ch = getchar();
    while(isdigit(ch));
    return ans * f;
}

inline bool isValid(char a,char b){
    if(a >= b) return false;
    if(isdigit(a) && isdigit(b)) return true;
    if((a >= 'a' && a <= 'z') && (b >= 'a' && b <= 'z')) return true;
    return false;
}

int main(){
//    freopen("expand.in", "r", stdin);
//    freopen("expand.out", "w", stdout);
    int p1 = read(), p2 = read(), p3 = read();
    string s;
    cin >> s;
    int len = s.length();
    for(int i=0; i<len; i++){
        if(s[i] == '-' && i != 0 && i != len-1 && isValid(s[i-1], s[i+1])){
            int add = (!isdigit(s[i-1]) && (p1 == 2)) * ('A' - 'a');
            char begin = s[i-1], end = s[i+1];
            if(p3 == 2) swap(begin, end);
            int d = (begin < end) ? 1 : -1;
            for(char k=begin; k!=end; k+=d){
                if(k == begin) continue;
                for(int j=1; j<=p2; j++){
                    if(p1 == 3) putchar('*');
                    else putchar(k+add);
                }
            } 
        }
        else putchar(s[i]);
    }
    return 0;
}

第三题 矩阵取数字游戏

题目描述

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n×mn \times mn×m的矩阵,矩阵中的每个元素ai,ja_{i,j}ai,j均为非负整数。游戏规则如下:

  1. 每次取数时须从每行各取走一个元素,共nnn个。经过mmm次后取完矩阵内所有元素;
  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
  3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值×2i\times 2^i×2i,其中iii表示第iii次取数(从111开始编号);
  4. 游戏结束总得分为mmm次取数得分之和。

帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

输入格式

输入文件包括n+1n+1n+1行:

111行为两个用空格隔开的整数nnn和mmm。

2∽n+12\backsim n+12n+1行为n×mn \times mn×m矩阵,其中每行有mmm个用单个空格隔开的非负整数。

输出格式

输出文件仅包含111行,为一个整数,即输入矩阵取数后的最大得分。

输入输出样例

输入 #1
2 3
1 2 3
3 4 2
输出 #1
82

说明/提示

NOIP 2007 提高第三题

数据范围:

60%的数据满足:1≤n,m≤301\le n, m \le 301n,m30,答案不超过101610^{16}1016
100%的数据满足:1≤n,m≤801\le n, m \le 801n,m80,0≤ai,j≤10000 \le a_{i,j} \le 10000ai,j1000

思路

当我简单的看完题面后发现是DP 但是一直把每个数字隔离分析没有想到用区间DP来处理

最后还是用了一手爆搜狂砍20分

正解 懒人解

1.

  • nn行最大得分和,每一行取数又不会影响到其他行,那么只要确保每一行得分最大,管好自家孩子就行了。(这个在动规中叫最优子结构)
  • 每次取数是在边缘取,那么每次取数完剩下来的元素一定是在一个完整的一个区间中,又是求最优解,区间DP应运而生

2.  

  • 思路其实大同小异:按行进行区间DP,我们可以设区间[L,R]的最大值为f[L][R],用一个数组p[n]来储存2^n的值,采用记忆化搜索的办法,
  • 设k=m-(R-L),可以得到状态转移方程:f[L][R]=max(num[L]*p[k]+dp(L+1,R),dp(L,R-1)+num[R]*p[k]),

西卡西!!

结果中数据竟然有超过2^80 妥妥爆long long (unsigned long long照样爆)

我们要使用高精度处理每一位数据(这才是这道题最难写的地方啊

当然如果你跟本蒟蒻一样懒而且觉得自己在赛场上能写出来高精度

完全可以用int128水过去

#include<bits/stdc++.h>
#define in(x) x=read()
#define MAXN 81
#define k m-(R-L)
#define bll __int128

using namespace std;

inline int read() 
{
    int X=0,w=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
    while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
    return X*w;
}

int n,m;
int num[MAXN];
bll ans,p[MAXN],f[MAXN][MAXN];

bll dp(int L,int R)//记忆化搜索 
{
    if(f[L][R]!=-1) return f[L][R];
    if(R-L>=1) f[L][R]=max(num[L]*p[k]+dp(L+1,R),dp(L,R-1)+num[R]*p[k]);
    else f[L][R]=num[L]*p[k];
    return f[L][R];
}

void print(bll x)
{
    if(!x) return;
    if(x) print(x/10);
    putchar(x%10+'0');
}

int main()
{
    in(n);in(m);
    p[0]=1;
    for(int i=1;i<=m;i++) p[i]=p[i-1]*2;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++) in(num[j]);
        memset(f,-1,sizeof(f));
        ans+=dp(1,m);
    }
    if(!ans) printf("0");
    else print(ans);
    return 0;
}

本着认真负责的态度 我将隔壁lrh老哥的高精度贴上去 让大家知道不努力学习就会被拉开的恐怖(偷懒的好处)

#include <bits/stdc++.h>
using namespace std;

const int N=85,mod=10000;
int n,m;
int ar[N];

struct node{
    int p[505], len;
    node() {
        memset(p, 0, sizeof p);
        len = 0;
    }  
    void print() {
        printf("%d", p[len]);  
        for (int i = len - 1; i > 0; i--) {  
            if (p[i] == 0) {
                printf("0000"); 
                continue;
            }
            for (int k = 10; k * p[i] < mod; k *= 10) 
                printf("0");
            printf("%d", p[i]);
        }
    }
}f[N][N],base[N],ans;

node operator + (const node &a,const node &b){
    node c; c.len = max(a.len, b.len); int x = 0;
    for (int i = 1; i <= c.len; i++) {
        c.p[i] = a.p[i] + b.p[i] + x;
        x = c.p[i] / mod;
        c.p[i] %= mod;
    }
    if (x > 0)
        c.p[++c.len] = x;
    return c;
}

node operator * (const node &a,const int &b){
    node c; c.len = a.len; int x = 0;
    for (int i = 1; i <= c.len; i++) {
        c.p[i] = a.p[i] * b + x;
        x = c.p[i] / mod;
        c.p[i] %= mod;
    }
    while (x > 0)
        c.p[++c.len] = x % mod, x /= mod;
    return c;
}
node max(const node &a,const node &b){
    if (a.len > b.len)
        return a;
    else if (a.len < b.len)
        return b;
    for (int i = a.len; i > 0; i--)
        if (a.p[i] > b.p[i])
            return a;
        else if (a.p[i] < b.p[i])
            return b;
    return a;
}

inline void basetwo(){
    base[0].p[1]=1,base[0].len=1;
    for(int i=1;i<=m+2;i++){
        base[i]=base[i-1]*2;
    }
}

inline int read(){
    int p=0,f=1;
    char ch=getchar();
    while(!isdigit(ch)) f*=(ch=='-')? -1:1,ch=getchar();
    do p=(p<<1)+(p<<3)+(ch^48),ch=getchar();
    while(isdigit(ch));
    return p*f;
}

int main(){
    n=read(),m=read();
    basetwo();
    while(n--){
        memset(f,0,sizeof f);
        for(int i=1;i<=m;i++) ar[i]=read();
        for(int i=1;i<=m;i++){
            for(int j=m;j>=i;j--){
                f[i][j] = max(f[i][j], f[i - 1][j] + base[m - j + i - 1] * ar[i - 1]); 
                f[i][j] = max(f[i][j], f[i][j + 1] + base[m - j + i - 1] * ar[j + 1]);
            }
        }
        node Max;
        for (int i = 1; i <= m; i++)
            Max = max(Max, f[i][i] + base[m] * ar[i]);
        ans = ans + Max; 
    }
    ans.print();
    return 0;
} 

第四题 树网的核

题目描述

T=(V,E,W)T=(V,E,W)T=(V,E,W)是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称TTT为树网(treebetwork),其中VVV,EEE分别表示结点与边的集合,WWW表示各边长度的集合,并设TTT有nnn个结点。

路径:树网中任何两结点aaa,bbb都存在唯一的一条简单路径,用d(a,b)d(a, b)d(a,b)表示以a,ba, ba,b为端点的路径的长度,它是该路径上各边长度之和。我们称d(a,b)d(a, b)d(a,b)为a,ba, ba,b两结点间的距离。

D(v,P)=min⁡{d(v,u)}D(v, P)=\min\{d(v, u)\}D(v,P)=min{d(v,u)}, uuu为路径PPP上的结点。

树网的直径:树网中最长的路径成为树网的直径。对于给定的树网TTT,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。

偏心距ECC(F)\mathrm{ECC}(F)ECC(F):树网T中距路径F最远的结点到路径FFF的距离,即

ECC(F)=max⁡{d(v,F),v∈V}\mathrm{ECC}(F)=\max\{d(v, F),v \in V\}ECC(F)=max{d(v,F),vV}

任务:对于给定的树网T=(V,E,W)T=(V, E, W)T=(V,E,W)和非负整数sss,求一个路径FFF,他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过sss(可以等于s),使偏心距ECC(F)ECC(F)ECC(F)最小。我们称这个路径为树网T=(V,E,W)T=(V, E, W)T=(V,E,W)的核(Core)。必要时,FFF可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。

下面的图给出了树网的一个实例。图中,A−BA-BAB与A−CA-CAC是两条直径,长度均为202020。点WWW是树网的中心,EFEFEF边的长度为555。如果指定s=11s=11s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为888。如果指定s=0s=0s=0(或s=1s=1s=1、s=2s=2s=2),则树网的核为结点FFF,偏心距为121212。

图片来自洛谷)

输入格式

nnn行。

111行,两个正整数nnn和sss,中间用一个空格隔开。其中nnn为树网结点的个数,sss为树网的核的长度的上界。设结点编号以此为1,2,…,n1,2,…,n1,2,,n。

从第222行到第nnn行,每行给出333个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“2472 4 7247”表示连接结点222与444的边的长度为777。

输出格式

一个非负整数,为指定意义下的最小偏心距。

输入输出样例

输入 #1
5 2
1 2 5
2 3 2
2 4 4
2 5 3
输出 #1
5
输入 #2
8 6
1 3 2
2 3 2 
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
输出 #2
5

说明/提示

40%40\%40%的数据满足:5≤n≤155 \le n \le 155n15
70%70\%70%的数据满足:5≤n≤805 \le n \le 805n80
100%100\%100%的数据满足:5≤n≤300,0≤s≤10005 \le n \le 300,0 \le s \le 10005n300,0s1000。边长度为不超过100010001000的正整数

NOIP 2007 提高第四题

小博文我对不起你啊 明明你都给我讲过我还是不会做啊(懒得写

考场搞了一手第三题后没时间把板子打出来了...

思路:

总之,要找核,先是要找树的直径(最长链),找树的直径只需要先从一个点(随便是什么)遍历,找到距离它最远的一个点,那么这个点肯定就是树的直径的一端了,从找到的这一端再遍历一次,找到距离这个端点最远的点,这个点就是另外一端了,这个应该都会搞

在遍历第二次的时候就将树改成了以直径一个端点为根的有根树,这样直径上每个点就可以通过父亲节点的记录来枚举路径

枚举过程中求出最小偏心距

总结:这个难度其实放在压轴题还算好...

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=3012;
int edge,n,s,head[N],vet[N*2],val[N*2],nex[N*2],a[N],fa[N],dis[N];
bool f[N];
inline void addedge(int u,int v,int c)
{
    nex[++edge]=head[u];
    head[u]=edge;
    vet[edge]=v;
    val[edge]=c;
}
inline void dfs(int u,int father)
{
    int e,v;
    for (e=head[u];v=vet[e],e;e=nex[e])
    if (v!=father)
    {
        fa[v]=u;
        dis[v]=dis[u]+val[e];
        dfs(v,u);
    }
}
inline void dfs1(int u,int father,int id)
{
    int e,v;
    for (e=head[u];v=vet[e],e;e=nex[e])
    if (v!=father&&!f[v])
    {
        a[id]=max(a[id],dis[v]-dis[u]);
        dfs1(v,u,id);
    }
}
int main()
{
    int x,y,z;
    scanf("%d%d",&n,&s);
    for (int i=1; i<n; i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);
        addedge(y,x,z);
    }
    dfs(1,0);
    int l=0,r=0;
    for (int i=1; i<=n; i++)
    if(dis[i]>dis[l])l=i;
    for (int i=1; i<=n ;i++)dis[i]=0;
    dfs(l,0);
    for (int i=1; i<=n; i++)
    if (dis[i]>dis[r]) r=i;//两次dfs求直径
    fa[l]=0;//注意一定要把根的父亲重置为0,因为在第一次dfs中它是一个结点
    for (int i=r; i; i=fa[i])
    f[i]=1;//把直径上的点标记
    for (int i=r; i; i=fa[i])
    dfs1(i,0,i);//求a[]
    int ans=0,mn=100000012;
    for (int i=r; i; i=fa[i])
        for (int j=i; j; j=fa[j])
        {
            if (dis[i]-dis[j]>s) break;//优化
            ans=max(dis[j],dis[r]-dis[i]);//特殊点
            for (int k=i; k!=fa[j]; k=fa[k])
            ans=max(ans,a[k]);
            mn=min(ans,mn);
        }
    printf("%d\n",mn);
    return 0;
}

最后 谢谢我前面的两位美女催促我写博客 不然我是不会写的哦

超谢谢你们的(╬ ̄皿 ̄)

猜你喜欢

转载自www.cnblogs.com/Heartbeat358/p/12208432.html