题面:https://www.cnblogs.com/Juve/articles/11523567.html

影子:

暴力方法:枚举每一对点暴力统计最小权

优化:考虑并查集,枚举每个点,如果没有被访问过,那么尝试把这两个点加到一个集合里

维护每一个点作为最小权时的树上路径的两个端点,合并时维护即可

将所有点按照权值从大到小排序,对于将当前点和与其相连的所有点依次合
并到一个集合中。并查集需要维护当前集合中的最长路径长度和对应的两个端点。在合并两个集合后,最终集合的最长路一定只有两类情况:一类是其中一个集合的最长路,一共有 2 种;一类是由两个集合的最长路的端点互相连接而成,一共有 2×2=4 种。需要用到最近公共祖先的算法预处理求两点在树上的距离,离线处理即可。每次合并并查集之后用当前点的权值乘以最长路的总长度来更新最优结果即可。即使这个点不在当前合并后的集合的最长路上也是没有问题的,因为如果这样的话,必然已经在之前得到了对应的结果,这次合并不会对最终结果产生影响。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int t,n,d[MAXN],ans=0;
int to[MAXN<<1],nxt[MAXN<<1],pre[MAXN],val[MAXN<<1],cnt=0;
inline void add(int u,int v,int w){
cnt++,to[cnt]=v,nxt[cnt]=pre[u],pre[u]=cnt,val[cnt]=w;
}
int f[MAXN][19],deep[MAXN],dis[MAXN];
void dfs(int x){
for(int i=1;i<=18;i++){
if(f[x][i-1])
f[x][i]=f[f[x][i-1]][i-1];
}
for(int i=pre[x];i;i=nxt[i]){
if(to[i]!=f[x][0]){
deep[to[i]]=deep[x]+1;
dis[to[i]]=dis[x]+val[i];
f[to[i]][0]=x;
dfs(to[i]);
}
}
}
int LCA(int x,int y){
if(deep[x]<deep[y]) swap(x,y);
for(int i=18;i>=0;i--)
if(deep[f[x][i]]>=deep[y])
x=f[x][i];
if(x==y) return x;
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
struct node{
int val,id;
friend bool operator < (node a,node b){
return a.val>b.val;
}
}p[MAXN];
bool vis[MAXN];
int fa[MAXN];
struct data{
int d,st,ed;
}q[MAXN];
int find(int x){
return fa[x]=(fa[x]==x?x:find(fa[x]));
}
void unionn(int x,int y){
x=find(x),y=find(y);
if(x!=y){
fa[y]=x;
data mx;
if(q[x].d>q[y].d) mx=q[x];
else mx=q[y];
int lca=LCA(q[x].st,q[y].st);
int dist=dis[q[x].st]+dis[q[y].st]-2*dis[lca];
if(mx.d<dist) mx=(data){dist,q[x].st,q[y].st};
lca=LCA(q[x].st,q[y].ed);
dist=dis[q[x].st]+dis[q[y].ed]-2*dis[lca];
if(mx.d<dist) mx=(data){dist,q[x].st,q[y].ed};
lca=LCA(q[x].ed,q[y].ed);
dist=dis[q[x].ed]+dis[q[y].ed]-2*dis[lca];
if(mx.d<dist) mx=(data){dist,q[x].ed,q[y].ed};
lca=LCA(q[x].ed,q[y].st);
dist=dis[q[x].ed]+dis[q[y].st]-2*dis[lca];
if(mx.d<dist) mx=(data){dist,q[x].ed,q[y].st};
q[x]=mx;
}
}
signed main(){
scanf("%lld",&t);
while(t--){
memset(dis,0,sizeof(dis));
memset(pre,0,sizeof(pre));
memset(f,0,sizeof(f));
memset(deep,0,sizeof(deep));
memset(vis,0,sizeof(vis));
ans=0;cnt=0;
scanf("%lld",&n);
for(int i=1;i<=n;++i){
fa[i]=i;
q[i]=(data){0,i,i};
scanf("%lld",&d[i]);
p[i]=(node){d[i],i};
}
for(int i=1,u,v,w;i<n;++i){
scanf("%lld%lld%lld",&u,&v,&w);
add(u,v,w),add(v,u,w);
}
sort(p+1,p+n+1);
deep[1]=1;
dfs(1);
for(int i=1;i<=n;++i){
int x=p[i].id;
vis[x]=1;
for(int j=pre[x];j;j=nxt[j]){
int y=to[j];
if(!vis[y]) continue;
unionn(x,y);
}
ans=max(ans,q[x].d*d[x]);
}
printf("%lld\n",ans);
}
return 0;
}

玫瑰花精:

可以考虑线段树。首先我们对区间[1..n]建立一棵线段树。对于每一个节点,

维护 4 个值。分别是 l,r,mid,p。

l表示在当前结点线段树所在区间最左边的花精所在的位置,r 表示最右边的花精所在的位置。

mid 表示在这个小区间[l,r]中的两只花精之间的最长距离除以 2 后的值。

p 表示取 mid 值时所在的紧邻的两只花精的中间位置,也就是在[l,r]中的答案值。

对于 1 询问:访问线段树的第一个节点,我们比较 l-1,n-r,mid 的值哪个更大,就选哪个,它们的答案依次是 1,n,p。

假设我们求得的位置是 fairy[x]。然后访问[fairy[x],fairy[x]]所在的线段树的叶子节点,初始化它的值,然后回溯,进行合并。

对于 tr[x].l 与 tr[x].r 可以通过两个儿子的 l,r 信息得出。

对于 tr[x].mid值,首先在左右儿子的 mid 值中去一个最大的值。

其次考虑一种情况,就是夹在两个线段之间的距离,可以通过(tr[x<<1|1].l-tr[x<<1].r)/2 的值得出在于 mid进行比较,然后 p 就随着 mid

的值的更新而更新。

对于 2 询问:访问询问花精所在的位置,直接将它的叶子节点[fairy[x],fairy[x]]删除,然后回溯时,再做一次合并操作。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int MAXN=2e5+5;
const int MAXM=1e6+5;
int n,m,pos[MAXM];
struct node{
int l,r,mid,p;
}tr[MAXN<<2];
int get_pos(){
if(tr[1].l==0) return 1;
int res=max(tr[1].l-1,max(n-tr[1].r,tr[1].mid));
if(res==tr[1].l-1) return 1;
else if(res==tr[1].mid) return tr[1].p;
else return n;
}
void pushup(int k){
if(tr[k<<1].l==0||tr[k<<1|1].l==0) tr[k].l=tr[k<<1].l+tr[k<<1|1].l;
else tr[k].l=tr[k<<1].l;
tr[k].r=max(tr[k<<1|1].r,tr[k<<1].r);
tr[k].mid=max(tr[k<<1].mid,tr[k<<1|1].mid);
int p1=tr[k<<1].r,p2=tr[k<<1|1].l;
if(p1&&p2) tr[k].mid=max(tr[k].mid,(p2-p1)/2);
if(tr[k].mid==tr[k<<1].mid) tr[k].p=tr[k<<1].p;
else if(tr[k].mid==(p2-p1)/2) tr[k].p=(p2+p1)/2;
else tr[k].p=tr[k<<1|1].p;
}
void update(int k,int l,int r,int pos){
if(l==r){
tr[k].mid=tr[k].p=0;
tr[k].l=tr[k].r=l;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) update(k<<1,l,mid,pos);
else update(k<<1|1,mid+1,r,pos);
pushup(k);
}
void change(int k,int l,int r,int pos){
if(l==r){
tr[k].mid=tr[k].p=tr[k].l=tr[k].r=0;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) change(k<<1,l,mid,pos);
else change(k<<1|1,mid+1,r,pos);
pushup(k);
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==1){
pos[x]=get_pos();
update(1,1,n,pos[x]);
printf("%d\n",pos[x]);
}else{
change(1,1,n,pos[x]);
pos[x]=0;
}
}
return 0;
}

最新文章

  1. oracle学习笔记系列------oracle 基本操作之基本函数的用法
  2. 对于多个列的转行(一个值均匀分布在两个列中),对于个别字段通过取别名,join方式解决。
  3. android 第三方 图表
  4. 自动化回归测试案例评价标准 MeRest
  5. .net winform软件自动更新
  6. php学习记录 易混淆
  7. 【WCF 2】理解WCF框架的简单小实例
  8. linux 命令grep
  9. bzoj 2095: [Poi2010]Bridges(二分法+混合图的欧拉回路)
  10. 技术贴:asp.net实现唯一账户在线 禁止同一帐号同时在线 asp.net实现您的帐号在别处登录,您已被迫下线!
  11. liunx tomcat多站点配置
  12. 阿里云AliYun表格存储(Table Store)相关案例
  13. C++调用C方法
  14. flutter初体验
  15. 什么是TensorFlow?
  16. BELLMEN-FORD普通
  17. FastCGI Error Number: 5 (0x80070005).
  18. vnc 搭建 转
  19. 20172302 《Java软件结构与数据结构》第九周学习总结
  20. POJ 1062 昂贵的聘礼(最短路)题解

热门文章

  1. 能轻松背板子的FWT(快速沃尔什变换)
  2. thinkphp 使用函数
  3. vuecli脚手架+vue+vuex实现vue驱动的demo。
  4. yolo自己的数据集中LabelImg的安装出现No module named &#39;libs.resources&#39;错误
  5. appium基础一:连接手机和appium-desktop定位元素
  6. P1280 尼克的任务 /// DP(选择性地)
  7. FineUI使用记录
  8. Spring 基于xml配置方式的AOP(8)
  9. C# IP正则表达式
  10. Map和Reduce函数