题面: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;
}

CSP-S模拟41影子,玫瑰花精题解的更多相关文章

  1. csp-s模拟测试41「夜莺与玫瑰·玫瑰花精·影子」

    夜莺与玫瑰 题解 联赛$T1$莫比乌斯$\%\%\%$ $dead$  $line$是直线 首先横竖就是$n+m$这比较显然 枚举方向向量 首先我们枚举方向向量时只枚举右下方向,显然贡献$*2$就是所 ...

  2. NOIP 模拟 $15\; \rm \text{玫瑰花精}$

    题解 \(by\;zj\varphi\) 一道线段树题目 这道题可以通过维护一棵线段树,线段树上的每个节点维护 \(\rm l,r,len,p\) 分别表示这段区间最左边的花精,最右边的花精,被两只花 ...

  3. [CSP模拟测试43、44]题解

    状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ...

  4. [CSP-S模拟测试]:影子(并查集+LCA)

    题目描述 一个人有很多的影子,新的旧的,他们不断消失重来.学者的影子在他苍白色的精神图景里成为了$n$个黑色的点,他们伸长的触手交叉形成了一颗黑色的树.假使每个影子点拥有一个权值$d_i$,黑色的树边 ...

  5. 蓝桥杯大学B组省赛2020模拟赛(一)题解与总结

    题目链接:https://www.jisuanke.com/contest/6516 A:题目: 我们称一个数是质数,而且数位中出现了 5 的数字是有趣的. 例如 5, 59, 457.求1到1000 ...

  6. WC2019 全国模拟赛第一场 T1 题解

    由于只会T1,没法写游记,只好来写题解了... 题目链接 题目大意 给你一个数列,每次可以任取两个不相交的区间,取一次的贡献是这两个区间里所有数的最小值,求所有取法的贡献和,对 \(10^9+7\) ...

  7. NOIP2017金秋冲刺训练营杯联赛模拟大奖赛第一轮Day2题解

    上星期打的...题有点水,好多人都AK了 T1排个序贪心就好了 #include<iostream> #include<cstring> #include<cstdlib ...

  8. CSPS模拟 41

    说不会鸽就不会鸽的 虽然是炸裂的一场 T1没读懂题,T23交了两个无脑暴力 (公式懒得打了 latex过于感人) T1 点阵内不重合的直线有多少条? 枚举斜率,那么“后继”不在点阵内的点可以作出一个贡 ...

  9. [7.18NOIP模拟测试5]砍树 题解(数论分块)

    题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...

随机推荐

  1. hibernate_04_hibernate多对多的关系映射

    1.实体类的多对多的关系映射 一个用户可以有多个角色 User.java public class User { private Long user_id; private String user_c ...

  2. win 7 下安装GIT(亲测有效)

    我首先是百度到了这个网站:https://git-scm.com/download/win 当然由于外网访问速度的缓慢 可以直接在百度搜索下载自己对应的版本 这个网站上有下载链接,你可以根据你的系统选 ...

  3. 命令学习_nslookup

    nslookup 域名 这是最常用最简单的用法,可以直接获得目标域名的IP地址和CNAME. 如下是A记录的返回情况 nslookup命令会采用先反向解释获得使用的DNS服务器的名称,上图中ns.gu ...

  4. VS2010-MFC(常用控件:组合框控件Combo Box)

    转自:http://www.jizhuomi.com/software/189.html 上一节讲了列表框控件ListBox的使用,本节主要讲解组合框控件Combo Box.组合框同样相当常见,例如, ...

  5. VS2010-MFC(对话框:文件对话框)

    转自:http://www.jizhuomi.com/software/173.html 一 文件对话框的分类       文件对话框分为打开文件对话框和保存文件对话框,相信大家在Windows系统中 ...

  6. dashboard服务

    1.上传镜像,并导入,打标签 2.创建dashboard的deployment和service apiVersion: extensions/v1beta1 kind: Deployment meta ...

  7. 1.关于Python的发展历史你知道吗?

    1989,为了度过圣诞假期,Guido开始编写Python语言编译器.Python这个名字来自Guido的喜爱的电视连续剧<蒙蒂蟒蛇的飞行马戏团>.他希望新的语言Python能够满足他在C ...

  8. 华为-eNSP模拟器路由器无法正常启动一直显示“#”

    问题项如截图: 解决方案: 1. 打开自己电脑的控制面板 -->> 系统和安全 -->> Windows Defender防火墙 (运行应用通过Windows防火墙) 2 .找 ...

  9. 授权指定ip访问mysql 服务器

      授权指定ip访问访问 授权ROOT使用密码1234从应用服务器主机连接到mysql服务器 mysql> GRANT ALL PRIVILEGES ON *.* TO 'root'@'xxx. ...

  10. Gabor filter for image processing and computer vision

    介绍 我们已经知道,傅里叶变换是一种信号处理中的有力工具,可以帮助我们将图像从空域转换到频域,并提取到空域上不易提取的特征.但是经过傅里叶变换后,图像在不同位置的频度特征往往混合在一起,但是Gabor ...