• NOIP2007提高组


    2020-1-18


    第一题 

    统计数字

    题目描述

    某次科研调查时得到了nnn个自然数,每个数均不超过1500000000(1.5×109)1500000000(1.5 imes 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 imes 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 imes mn×m的矩阵,矩阵中的每个元素ai,ja_{i,j}ai,j均为非负整数。游戏规则如下:

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

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

    输入格式

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

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

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

    输出格式

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

    输入输出样例

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

    说明/提示

    NOIP 2007 提高第三题

    数据范围:

    60%的数据满足:1≤n,m≤301le n, m le 301n,m30,答案不超过101610^{16}1016
    100%的数据满足:1≤n,m≤801le 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
    ",mn);
        return 0;
    }

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

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

  • 相关阅读:
    文件上传跨域解决方案-jQuery-File-Upload
    求数列的的增幅,已知起始列和结束列,中间阶梯数
    mud那些坑 find_object问题
    jquery mCustomScrollbar使用
    JS的Date对象
    JS获取当前日期
    Go数组求和
    Go九九乘法表
    Go语言基础之time包
    Go语言基础之流程控制
  • 原文地址:https://www.cnblogs.com/Heartbeat358/p/12208432.html
Copyright © 2020-2023  润新知