• B. 夜空


    仰望夜空,你就知道,我来自哪里——gfdfy

    B. 夜空

     题目链接(题面依旧有问题):http://10.1.6.216/contest/18/problem/108

    题目背景

    dfy

    是一个喜欢仰望星空的帅气少年,一天晚上,他一如既往地躺在草地上看星星。看着它们有亮的有暗的,不由得突发奇想,想要考考你。

    题目描述

    dfy

    对每一颗星星赋予了一个亮度wi,并且他将连在一起会更加明亮的星星连在了一起。由于dfy审美独特,他将所有的星星连成了一棵树。在观赏星星的同时,dfy

    想要时刻知道哪个点在一定范围内是最亮的。

    简而言之,题意是这样的:给定n

    个点,并给出每个点的点权。同时给出n1条无向边,保证将所有的点连成一棵树。同时有m

    次询问,每次询问输入两个点的编号。需要你对于每一次询问,输出两点之间最短路径上的最大点权。

    输入格式

    第一行输入n,m

    ,含义如题目描述。

    第二行有n

    个数,代表n

    个点的点权。

    后面n1

    行,有n1次输入,每次输入x,y,意为从x到y

    连有一条无向边。

    紧接着m

    行,有m次输入,每次输入x,y,即m

    次询问。

    输出格式

    每一行对应一次询问输出。

    输入输出样例

    样例输入1

    5 3
    1 2 3 4 5
    1 2
    1 3
    2 4
    2 5
    1 2
    1 4
    3 5

    样例输出1

    2
    4
    5

    样例输入2

    10 10
    1 1 1 7 8 5 5 7 4 6 
    1 2
    1 3
    2 4
    1 5
    2 6
    3 7
    3 8
    6 9
    4 10
    6 1
    4 7
    9 1
    2 7
    4 7
    3 1
    7 3
    7 8
    3 9
    3 3
    

    样例输出2

    5
    7
    5
    5
    7
    1
    5
    7
    5
    1
    

    样例解释

    在第一个样例中,从1

    号点到2号点途径1,2,其中最大点权是2。从1号点到4号点途径1,2,4,其中最大点权是4。从3号点到5号点途径1,2,3,5,其中最大点权是5

    数据范围

    对于30

    的数据,n100,m100

    对于50

    的数据,n2000,m2000

    对于70

    的数据,n2000,m200000,0<wi1000000000

    对于100

    的数据,n200000,m200000,0<wi264。

    主要思路:

    当时做这道题的时候就觉得这是一个lca,但是我们发现他还要在lca的过程中保存路径上的最大值,正解肯定是先预处理跳跃过程中经过的最大值,然后在询问的时候O(1)求最大值,但是由于本人蒟蒻一只,所以.....很明显的,这种预处理过程我并不会写,所以我就选择一步一步往上跳,每次跳的时候访问这个节点并且求出当前的最大值。每次询问每次lca一遍,最后就可以判断出路径上的最大值。70分~~

    很明显的超出时间复杂度了qwq

    先展示一下自己的70分代码,然后我们再来分析一下正解的做法,并顺便复习一下lca,如果等我把所有的题目都整理完还有时间,那么我们就一起来做一下出题人推荐的题目(与此题类似,据TA所说这个题目是那道题的简化版,好像只有绿?)

    #include<bits/stdc++.h>
    using namespace std;
    
    int n,m;
    vector<int> v[202000];
    int fa[200005];
    int dep[200005];
    int a[5020000];
    
    void add(int x,int y)
    {
        v[x].push_back(y);
    }
    
    void dfs(int x)
    {
        for(int i=0;i<v[x].size();i++){
            int y=v[x][i];
            if(y==fa[x]) continue;
            fa[y]=x;
            dep[y]=dep[x]+1;
            dfs(y);
        }
    }
    
    int maxn=-999;
    
    int lca(int x,int y)
    {
        if(dep[x]<dep[y]) swap(x,y);
        maxn=max(a[x],a[y]);
        while(dep[x]>dep[y])
        {
            x=fa[x];
            maxn=max(maxn,a[x]);
        }
        
        if(x==y) return maxn;
        
        while(x!=y)
        {
            x=fa[x];
            y=fa[y];
            maxn=max(maxn,a[x]);
            maxn=max(maxn,a[y]);
        }
        
        return maxn;
        
    }
    int x,y;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",a+i);
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);
            add(y,x);
        }
        dep[1]=1;
        dfs(1);
        
        /*for(int j=0;j<=64;j++){
            for(int i=1;i<=n;i++){
                fa[i][j+1]=fa[fa[i][j]][j];
            }
        }*/
        
        while(m--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            printf("%d
    ",lca(x,y));
        }
        return 0;
    }

    std70:(出题人的70分代码)

    可能没有想我这样啥的人用正解(lca)打了70分的暴力qwq

    其实对于70分的数据,我们不需要用lca就可以实现

    我们n*n预处理所有点之间的最大值,对于每次询问,我们直接O(1)输出答案即可

    出题人的代码:(链式前向星存图)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<ctime>
    #include<map>
    #include<vector>
    using namespace std;
    typedef unsigned long long ull;
    inline ull read(){
        ull x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    const int N=2005,M=2000005;
    int n,m,tot;
    ull w[N],maxx[N][N];
    int ver[M],Next[M],head[N],d[N];
    bool v[N][N];
    queue<int> q;
    void add(int x,int y){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
    }
    void bfs(int a){
        maxx[a][a]=w[a];
        //maxx[x][y]表示从节点x到节点y路径上的最大值 
        q.push(a);
        v[a][a]=1;
        q.push(a);
        while(!q.empty()){
            int x=q.front();
            q.pop();
            for(int i=head[x];i;i=Next[i]){
                int y=ver[i];
                if(v[a][y]) continue;
                v[a][y]=1;
                maxx[a][y]=max(maxx[a][x],w[y]);
                q.push(y);
            }
        }
    }
    int main()
    {
    //    freopen("dfy.in","r",stdin);
    //    freopen("dfy.ans","w",stdout);
        n=read();m=read();
        for(int i=1;i<=n;i++){
            w[i]=read();
        }
        for(int i=1,x,y;i<=n-1;i++){
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        for(int i=1;i<=n;i++){
            bfs(i);
        }
        for(int i=1,x,y;i<=m;i++){
            x=read();y=read();
            printf("%llu
    ",maxx[x][y]);
        }
        return 0;
    }

    自己敲的70分正解代码:(vector存图)

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    const int N=2005,M=2000005;
    bool vis[N][N];
    ull w[N],maxx[N][N];
    queue<int> q;
    vector<int> v[N];
    
    void add(int x,int y)
    {
        v[x].push_back(y);
    }
    
    inline ull read(){
        ull x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    
    void bfs(int a)
    {
        maxx[a][a]=w[a];
        q.push(a);
        vis[a][a]=1;
    //    q.push(a);
        while(!q.empty() )
        {    
            int x=q.front() ;
            q.pop() ;
            for(int i=0;i<v[x].size();i++)
            {
                int y=v[x][i];
                if(vis[a][y]==1) continue;
                vis[a][y]=1;
                maxx[a][y]=max(maxx[a][x],w[y]);
                q.push(y); 
            }
        } 
    }
    
    int n,m;
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++) w[i]=read();
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        
        for(int i=1;i<=n;i++)
        {
            bfs(i);//对于每个节点都要预处理它到
            //其他所有节点的最大值 
        }
        
        for(int i=1;i<=m;i++)
        {
            int x,y;
            x=read();y=read();
            printf("%llu
    ",maxx[x][y]);
        }
        return 0;
    } 

    100分正解:

    其实在上面就已经说过了,正解就是lca+对点权的预处理。lca相信大家都不陌生(我相信),所以我们就直接从对于点权的预处理开始讲起。其实有了lca的前置知识,我们发现,由于我们是跳跃式进行lca查询的,所以我们也可以按照lca的方式,采取st表的方法对点权进行求解,设置一个变量maxx[x][i]表示节点x的第 2^i 个祖先与它之间的路径最大值,注意断句理解:节点x的,第 2^i 个祖先,与它之间的,路径的,最大值。(是不是感觉好理解了一点?如果 还是不行就建议多读几遍吧~),这样我们就按照lca的方式进行处理就好啦:

    maxx[y][j]=max(maxx[y][j-1],maxx[f[y][j-1]][j-1]);

    其实这里是采用的st表的方法来求的maxx,好像不是lca?

    算了,反正大致就是这个意思了,但是注意这里面还有一些坑点:

    数据范围是2^64,但是我们发现及时是unsigned long long也知道(2^64)-1的范围,而且据毒瘤出题人说他出的还是极限数据,所以这肯定会爆空间的,也就是说当输入数据是2^64时,输入数据会变成0(爆数据范围了嘛)。但是由于我们求的又是最大值,肯定要用到max函数,所以这里有一下两种方法:

    1.对于0特殊处理(std采用的方法)

    2.在输入的时候我们就先对数据进行处理,所有数据都减一,这样数据范围就可以贴合unsigned long long 的数据范围了,然后在最后输出答案的时候再加上1(但是我们发现这样还是要对2^64进行特殊处理qwq)

    std100(链式前向星存图+附赠数据一份):

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<queue>
    #include<ctime>
    #include<map>
    #include<vector>
    using namespace std;
    typedef unsigned long long ull;
    inline ull read(){
        ull x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    const int N=200005,M=400005;
    int n,m,tot;
    ull w[N],maxx[N][25];
    int ver[M],Next[M],head[N],d[N],f[N][25];
    queue<int> q;
    void add(int x,int y){
        ver[++tot]=y;
        Next[tot]=head[x];
        head[x]=tot;
    }
    void bfs(){
        d[1]=1;
        q.push(1);
        while(!q.empty()){
            int x=q.front();
            q.pop(); 
            for(int i=head[x];i;i=Next[i]){
                int y=ver[i];
                if(d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                if(w[x]==0||w[y]==0) maxx[y][0]=0;
                else maxx[y][0]=max(w[x],w[y]);
                for(int j=1;j<=23;j++){
                    f[y][j]=f[f[y][j-1]][j-1];
                }
                for(int j=1;j<=23;j++){
                    if(maxx[y][j-1]==0||maxx[f[y][j-1]][j-1]==0) maxx[y][j]=0;
                    else maxx[y][j]=max(maxx[y][j-1],maxx[f[y][j-1]][j-1]);
                }
                q.push(y);
            }
        }
    }
    void lca(int x,int y){
        if(d[x]<d[y]) swap(x,y);
        if(w[x]==0||w[y]==0){
            printf("18446744073709551616
    ");
            return;
        }
        ull maxn=max(w[x],w[y]);
        int k=log(d[x]-d[y])/log(2)+1;
        for(int i=k;i>=0;i--){
            if(d[f[x][i]]>=d[y]){
                if(maxx[x][i]==0){
                    maxn=0;
                    break;
                }
                maxn=max(maxn,maxx[x][i]);
                x=f[x][i];
            }
        }
        if(maxn==0){
            printf("18446744073709551616
    ");
            return;
        }
        if(x==y){
            printf("%llu
    ",maxn);
            return;
        } 
        k=log(d[x])/log(2)+1;
        for(int i=k;i>=0;i--){
            if(f[x][i]!=f[y][i]){
                if(maxx[x][i]==0||maxx[y][i]==0){
                    maxn=0;
                    break;
                }
                else{
                    maxn=max(maxn,max(maxx[x][i],maxx[y][i]));
                }
                x=f[x][i];
                y=f[y][i];
            }
        }
        if(maxn==0){
            printf("18446744073709551616
    ");
            return;
        }
        if(maxx[x][0]==0||maxx[y][0]==0){
            maxn=0;
        }
        else{
            maxn=max(maxn,max(maxx[x][0],maxx[y][0]));
        }
        if(maxn==0){
            printf("18446744073709551616
    ");
            return;
        }
        else{
            printf("%llu
    ",maxn);
        }
    }
    int main()
    {
    //    freopen("dfy8.in","r",stdin);
    //    freopen("dfy8.out","w",stdout);
        for(int i=0;i<=N-2;i++){
            for(int j=0;j<=23;j++){
                maxx[i][j]=1;
            }
        }
        n=read();m=read();
        for(int i=1;i<=n;i++){
            w[i]=read();
        }
    //    for(int i=1;i<=n;i++){
    //        printf("%llu ",w[i]);
    //    }
    //    printf("
    ");
        for(int i=1,x,y;i<=n-1;i++){
            x=read();y=read();
            add(x,y);
            add(y,x);
        }
        bfs();
    //    for(int i=1;i<=n;i++){
    //        for(int j=0;j<=3;j++){
    //            printf("%llu ",maxx[i][j]);
    //        }
    //        printf("
    ");
    //    }
    //    for(int i=1;i<=n;i++){
    //        printf("%llu ",d[i]);
    //    }
    //    printf("
    ");
        for(int i=1,x,y;i<=m;i++){
            x=read();y=read();
            if(x==y){
                if(w[x]==0){
                    printf("18446744073709551616
    ");
                }
                else{
                    printf("%llu
    ",w[x]);
                }
            }
            else lca(x,y);
        }
        return 0;
    }
    /*
    5 10
    18446744073709551616 2 3 4 5
    1 2
    1 3
    2 4
    2 5
    1 2
    1 3
    1 4
    1 5
    2 3
    2 4
    2 5
    3 4
    3 5
    4 5
    */

    自己敲的代码:(vector存图):

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef unsigned long long ull;
    typedef long long ll;
    
    typedef unsigned long long ull;
    inline ull read() {
        ull x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)) {
            if(ch=='-') f=-1;
            ch=getchar();
        }
        while(isdigit(ch)) {
            x=x*10+ch-48;
            ch=getchar();
        }
        return x*f;
    }
    
    const int N=200005,M=400005;
    int n,m,d[N];
    ull w[N],maxx[N][25];
    vector<int> v[N];
    queue<int> q;
    int f[N][25];
    
    
    
    void bfs() {
        d[1]=1;//因为树上所有节点我们都是要遍历一遍的,
        //所以选取哪个点作为起点都是可以的
    //    int y;
        q.push(1);
        while(!q.empty() ) {
            int x=q.front() ;
            q.pop() ;
            for(int i=0; i<v[x].size() ; i++) {
                int y=v[x][i];
                if(d[y]) continue;
                d[y]=d[x]+1;
                f[y][0]=x;
                if(w[x]==0||w[y]==0) maxx[y][0]=0;
                else maxx[y][0]=max(w[x],w[y]);
    
                for(int j=1; j<=23; j++) {
                    f[y][j]=f[f[y][j-1]][j-1];
                }
                for(int j=1; j<=23; j++) {
                    if(maxx[y][j-1]==0||maxx[f[y][j-1]][j-1]==0) maxx[y][j]=0;
                    else maxx[y][j]=max(maxx[y][j-1],maxx[f[y][j-1]][j-1]);
                }
                q.push(y);
            }
            
        }
    }
    
    
    void add(int x,int y) {
        v[x].push_back(y);
    }
    
    void lca(int x,int y) {
    
    //    cout<<x<<" "<<y<<" :"<<endl;
        if(d[x]<d[y]) swap(x,y);
        
        if(w[x]==0||w[y]==0) {
            printf("18446744073709551616
    ");
            return ;
        }
    
        ull maxn=max(w[x],w[y]);
    //    cout<<maxn<<" 1"<<endl;
    
        int k=log(d[x]-d[y])/log(2)+1;
    
        for(int i=k; i>=0; i--) {
            if(d[f[x][i]]>=d[y]) {//1.需要取等!!!我调了好长时间才发现这个bug
                if(maxx[x][i]==0) {
                    maxn=0;
    //                cout<<maxn<<" 2"<<endl;
                    break;
                }
                maxn=max(maxn,maxx[x][i]);
            
    //        cout<<maxn<<" 3"<<endl;
    //        cout<<"x: "<<x<<endl;
            x=f[x][i];
    //        cout<<x<<" **** "<<endl;
            }
            
        }
    //    cout<<"x: "<<x<<" "<<"y: "<<y<<endl;
    //    cout<<maxn<<" 4"<<endl;
        
        if(maxn==0)
        {
            printf("18446744073709551616
    ");
            return;
        }
    //    cout<<maxn<<endl;
        if(x==y)
        {
            printf("%llu
    ",maxn);
            return;
        }
        
        k=log(d[x])/log(2)+1;
        
        for(int i=k;i>=0;i--)
        {
            if(f[x][i]!=f[y][i]){
                if(maxx[x][i]==0||maxx[y][i]==0){
                    maxn=0;
    //                cout<<maxn<<" 5"<<endl;
                    break;
                }
                else
                {
                    maxn=max(maxn,max(maxx[x][i],maxx[y][i]));
    //                cout<<maxn<<" 6"<<endl;
                }
                x=f[x][i];
                y=f[y][i];
            }
        }
        
        if(maxn==0) 
        {
            printf("18446744073709551616
    ");
            return ;
        }
        
    //    cout<<maxn<<" maxn"<<endl;
        if(maxx[x][0]==0||maxx[y][0]==0)
        {
            printf("18446744073709551616
    ");
            return;
        }
        
        else
        {
            maxn=max(maxn,max(maxx[x][0],maxx[y][0]));
        }
        if(maxn==0)
        {
            printf("18446744073709551616
    ");
            return;
        }
        else printf("%llu
    ",maxn);
    }
    
    
    
    
    int main() {
    
        for(int i=0;i<=N-2;i++){
            for(int j=0;j<=23;j++){
                maxx[i][j]=1;
            }
        }    
        n=read();
        m=read();
        for(int i=1; i<=n; i++) w[i]=read();
        for(int i=1; i<=n-1; i++) {
            int x,y;
            x=read();
            y=read();
            add(x,y);
            add(y,x);
        }
    
        bfs();
        
        /*int k=24;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=k;j++)
            cout<<maxx[i][j]<<" ";
            cout<<endl;
        }
        cout<<endl;
        k=24;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=k;j++)
            cout<<f[i][j]<<" ";
            cout<<endl;
        }*/
    
        for(int i=1; i<=m; i++) {
            int x,y;
            x=read();
            y=read();
            if(x==y) {
                if(w[x]==0) printf("18446744073709551616
    ");//2.一开始这里的==写成=了,调了20min  qwq
                else printf("%llu
    ",w[x]);
    //            cout<<w[x]<<endl;
            }
    
            else lca(x,y);
        }
        return 0;
    }

    不行不行,这个题太恶心了,我写了一个多小时,调了一个多小时,终于AC了qwq

    长代码出现错误真的太致命了,写的时候一定一定要小心仔细!!不然最后找到哪里出错都是问题qwq

    -----------end-----------

  • 相关阅读:
    ActiveMQ的作用总结(应用场景及优势)
    深入剖析ThreadLocal实现原理以及内存泄漏问题
    Java ThreadLocal 原理分析
    关于iview ui的"Page分页"组件的使用
    使用TortoiseGit操作分支的创建与合并
    乐观锁与悲观锁的区别
    Linux下如何安装MongoDB及配置
    centos8安装图解(超详细教程)
    SpringBoot整合MongoDB
    PPT学习---醍醐灌顶!你对PPT的理解过时了!
  • 原文地址:https://www.cnblogs.com/yxr001002/p/14057057.html
Copyright © 2020-2023  润新知