BSG白山极客挑战赛D 解题报告

本文介绍了一个关于树结构的性质:对于树T的任意一条直径(a,b),∀x∈T,必然有max(|(a,x)|,|(b,x)|)≥max{|(x,y)}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题关键当然就在那个非常神的性质。

其实,对于一棵树来说,我们在上面随便找一个点(可以是边上的点任意一点),也就是可以选无穷多个点,那么距离这个点最远的点一定是一条直径的一端。且任意一条直径都存在一个端点是距离这个点最远的点。
我们考虑距离任意一点x最远的点y,假设有一条直径是(a,b)。(下面我们用(a,b)来表示两点之间的路径,用|(a,b)|来表示这条路径的长度)。
那么我们分两种情况考虑。
如果(a,b)(x,y)=,如图。因为y距离x最远,所以|(d,y)||(d,a)|,|(d,y)||(d,b)|;因为(a,b)是直径,所以|(b,c)||(c,y)|,|(a,c)||(c,y)|,所以|(d,y)||(b,d)|>|(b,c)||(c,y)|>|(d,y)|,产生了矛盾,所以这种情况其实是不存在的。
如果(a,b)(x,y),如图。因为y距离x最远,所以|(d,y)||(d,b)|,因为(a,b)是直径,所以|(d,y)||(d,b)|,所以|(d,y)|=|(d,b)|。即(a,y)也是一条直径。

根据这个性质就有一个经典的找直径的算法,就是随便找一个点找离它最远的点,那么它必然是一条直径的一端,而离它最远的点就是直径的另一端了,所以只需要两边dfs/bfs即可。

那么这一题需要说明的是对于两个点集S、T,任选xS,yT,则距离最远的(x,y)中存在y{a,b}((a,b)是T的一条直径),那么对称的x也一样。
其实在S中任选一个点x出发,设其在T中距离最远的点为y,(x,y)上最靠近x的存在于T中两点路径上的点为z。那么y显然必须是离z最远的点,而根据上面的分析,一条直径必然有一个端点是离z最远的点。

那么这个问题就变成了求出一段区间的直径,题解说是可以用线段树O(logn)预处理,O(1)查询,我表示完全没看懂。。
我的做法是先用st表预处理出lca,因为合并两个区间的时候显然需要O(1)查询两点距离什么的。然后再用st表预处理出长为2i的区间的直径。时间复杂度O(6nlogn)O(6m)

代码:

#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
void in(int &x){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    for(x=0;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
const int N=1e5+5;
int next[N<<1],succ[N<<1],w[N<<1],ptr[N],etot=1;
void addedge(int from,int to,int wt){
    next[etot]=ptr[from],ptr[from]=etot,succ[etot]=to,w[etot++]=wt;
}

const int Log=18;
int fa[N],depth[N];
int d[N<<1],dfn[N];
int Min(const int &a,const int &b){
    return depth[a]<depth[b]?a:b;
}
int dtot=1;
int dis[N];
int stack[N],cur[N];
void dfs(){
    stack[0]=1;
    depth[1]=1;
    for(int top=1,node;top--;){
        node=stack[top];
        //printf("-----%d----\n",node);
        if(cur[node]!=ptr[node]){
            d[dtot]=node;
            dfn[node]=dtot++;
        }
        if(cur[node]){
            ++top;
            if(succ[cur[node]]!=fa[node]){
                depth[succ[cur[node]]]=depth[node]+1;
                dis[succ[cur[node]]]=dis[node]+w[cur[node]];
                fa[succ[cur[node]]]=node;
                stack[top++]=succ[cur[node]];
            }
            cur[node]=next[cur[node]];
        }
    }
}
int lca[Log][N<<1];
int lg[N<<1];
int querydis(int a,int b){
    if(dfn[a]>dfn[b])swap(a,b);
    int tmplg=lg[dfn[b]-dfn[a]+1],x=Min(lca[tmplg][dfn[a]],lca[tmplg][dfn[b]-(1<<tmplg)+1]);
    //printf("Min%d:(%d,%d)\n",tmplg,dfn[a],dfn[b]-(1<<tmplg)+1);
    //printf("dis(%d,%d),%d=%d\n",a,b,x,dis[a]+dis[b]-(dis[x]<<1));
    return dis[a]+dis[b]-(dis[x]<<1);
}
struct AS{
    int a[2];
}st[Log][N];
AS merge(const AS & u,const AS &v){
    int maxdis=querydis(u.a[0],u.a[1]),tmp;
    AS ans=u;
    if((tmp=querydis(v.a[0],v.a[1]))>maxdis){
        maxdis=tmp;
        ans=v;
        //printf("Get:%d\n",maxdis);
    }
    for(int i=2;i--;)
        for(int j=2;j--;)
            if((tmp=querydis(u.a[i],v.a[j]))>maxdis){
                maxdis=tmp;
                ans=(AS){u.a[i],v.a[j]};
            }
    //printf("merge((%d,%d),(%d,%d))=(%d,%d)\n",u.a[0],u.a[1],v.a[0],v.a[1],ans.a[0],ans.a[1]);
    return ans;
}
AS query(int l,int r){
    int tmplg=lg[r-l+1];
    //printf("[%d,%d]\n",l,r);
    //printf("merge(%d,(%d,%d))\n",tmplg,l,r-(1<<tmplg)+1);
    //printf("%d %d\n",st[tmplg][l].a[0],st[tmplg][l].a[1]);
    return merge(st[tmplg][l],st[tmplg][r-(1<<tmplg)+1]);
}
int main(){
    freopen("51noded.in","r",stdin);
    //freopen("51noded.out","w",stdout);
    int n;
    in(n);
    int x,y,z;
    for(int i=n;--i;){
        in(x),in(y),in(z);
        addedge(x,y,z),addedge(y,x,z);
    }

    dtot=1;
    for(int i=n;i;--i)cur[i]=ptr[i];
    dfs();

    //cout<<dfn[65536]<<endl;
    //cout<<d[68211]<<endl;
    /*puts("----dfn----");
    for(int i=1;i<dtot;++i)printf("%d ",d[i]);
    puts("");*/

    //cout<<d[68211]<<endl;
    for(int i=dtot;--i;)lca[0][i]=d[i];
    for(int i=1,j=0;i<dtot;++i){
        lg[i]=j;
        if(i==1<<j+1)++j;
    }
    //cout<<lca[0][68211]<<endl;
    for(int j=1;j<Log;++j)
        for(int i=dtot-(1<<j);i>0;--i)
            lca[j][i]=Min(lca[j-1][i],lca[j-1][i+(1<<j-1)]);

    /*for(int j=0;j<Log;++j)
        for(int i=dtot-(1<<j);i>0;--i)
            printf("lca(%d,%d)=%d\n",j,i,lca[j][i]);*/

    for(int i=1;i<=n;++i)st[0][i]=(AS){i,i};
    for(int j=1;j<Log;++j)
        for(int i=n-(1<<j)+1;i>0;--i)
            st[j][i]=merge(st[j-1][i],st[j-1][i+(1<<j-1)]);
    //printf("%d %d\n",st[15][1].a[0],st[15][1].a[1]);
    //printf("%d %d\n",st[0][65895].a[0],st[0][65895].a[1]);
    //printf("%d %d\n",st[0][65896].a[0],st[0][65896].a[1]);
    //cout<<Min(lca[0][68211],lca[0][68211])<<endl;
    //testquery(65895,65895);

    int m;
    in(m);
    int a,b,c,d;
    AS s,t;
    while(m--){
        in(a),in(b),in(c),in(d);
        s=query(a,b),t=query(c,d);
        //printf("s=%d,%d\n",s.a[0],s.a[1]);
        //printf("t=%d,%d\n",t.a[0],t.a[1]);
        int maxdis=0;
        for(int i=2;i--;)
            for(int j=2;j--;)
                maxdis=max(maxdis,querydis(s.a[i],t.a[j]));
        printf("%d\n",maxdis);
    }
}

总结:
①一定要记得检查数组大小!
②对于树T的任意一条直径(a,b),xT,必然有max(|(a,x)|,|(b,x)|)max{|(x,y)}(yT).

基于据挖掘的音乐推荐系统设计与实现 需要一个代码说明,不需要论文 采用python语言,django框架,mysql据库开发 编程环境:pycharm,mysql8.0 系统分为前台+后台模式开发 网站前台: 用户注册, 登录 搜索音乐,音乐欣赏(可以在线进行播放) 用户登陆时选择相关感兴趣的音乐风格 音乐收藏 音乐推荐算法:(重点) 本课题需要大量用户行为(如播放记录、收藏列表)、音乐特征(如音频特征、歌曲元据)等据 (1)根据用户之间相似性或关联性,给一个用户推荐与其相似或有关联的其他用户所感兴趣的音乐; (2)根据音乐之间的相似性或关联性,给一个用户推荐与其感兴趣的音乐相似或有关联的其他音乐。 基于用户的推荐和基于物品的推荐 其中基于用户的推荐是基于用户的相似度找出相似相似用户,然后向目标用户推荐其相似用户喜欢的东西(和你类似的人也喜欢**东西); 而基于物品的推荐是基于物品的相似度找出相似的物品做推荐(喜欢该音乐的人还喜欢了**音乐); 管理员 管理员信息管理 注册用户管理,审核 音乐爬虫(爬虫方式爬取网站音乐据) 音乐信息管理(上传歌曲MP3,以便前台播放) 音乐收藏管理 用户 用户资料修改 我的音乐收藏 完整前后端源码,部署后可正常运行! 环境说明 开发语言:python后端 python版本:3.7 据库:mysql 5.7+ 据库工具:Navicat11+ 开发软件:pycharm
MPU6050是一款广泛应用在无人机、机器人和运动设备中的六轴姿态传感器,它集成了三轴陀螺仪和三轴加速度计。这款传感器能够实时监测并提供设备的角速度和线性加速度据,对于理解物体的动态运动状态至关重要。在Arduino平台上,通过特定的库文件可以方便地与MPU6050进行通信,获取并解析传感器据。 `MPU6050.cpp`和`MPU6050.h`是Arduino库的关键组成部分。`MPU6050.h`是头文件,包含了定义传感器接口和函声明。它定义了类`MPU6050`,该类包含了初始化传感器、读取据等方法。例如,`begin()`函用于设置传感器的工作模式和I2C地址,`getAcceleration()`和`getGyroscope()`则分别用于获取加速度和角速度据。 在Arduino项目中,首先需要包含`MPU6050.h`头文件,然后创建`MPU6050`对象,并调用`begin()`函初始化传感器。之后,可以通过循环调用`getAcceleration()`和`getGyroscope()`来不断更新传感器读。为了处理这些原始据,通常还需要进行校准和滤波,以消除噪声和漂移。 I2C通信协议是MPU6050与Arduino交互的基础,它是一种低引脚的串行通信协议,允许多个设备共享一对据线。Arduino板上的Wire库提供了I2C通信的底层支持,使得用户无需深入了解通信细节,就能方便地与MPU6050交互。 MPU6050传感器的据包括加速度(X、Y、Z轴)和角速度(同样为X、Y、Z轴)。加速度据可以用来计算物体的静态位置和动态运动,而角速度据则能反映物体转动的速度。结合这两个据,可以进一步计算出物体的姿态(如角度和角速度变化)。 在嵌入式开发领域,特别是使用STM32微控制器时,也可以找到类似的库来驱动MPU6050。STM32通常具有更强大的处理能力和更多的GPIO口,可以实现更复杂的控制算法。然而,基本的传感器操作流程和据处理原理与Arduino平台相似。 在实际应用中,除了基本的传感器读取,还可能涉及到温度补偿、低功耗模式设置、DMP(字运动处理器)功能的利用等高级特性。DMP可以帮助处理传感器据,实现更高级的运动估计,减轻主控制器的计算负担。 MPU6050是一个强大的六轴传感器,广泛应用于各种需要实时运动追踪的项目中。通过 Arduino 或 STM32 的库文件,开发者可以轻松地与传感器交互,获取并处理据,实现各种创新应用。博客和其他开源资源是学习和解决问题的重要途径,通过这些资源,开发者可以获得关于MPU6050的详细信息和实践指南
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值