题面: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. SQL Server ODBC 解决方案

    { } { }

  2. Windows 隐藏控制台

    #pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"& ...

  3. 云HBase备份恢复,为云HBase数据安全保驾护航

    摘要: 介绍了阿里云HBase自研备份恢复功能的基本背景以及基本原理架构和基本使用方法.   云HBase发布备份恢复功能,为用户数据保驾护航.对大多数公司来说数据的安全性以及可靠性是非常重要的,如何 ...

  4. 数论+线性dp——cf1174A

    直接推公式没有推出来 看了题解才会做.. 首先能够确定前面几个数的gcd一定是2^j * 3^k, 其中k<=1 那么可以用dp[i][j][k]来表示到第i位的gcd是2^j*3^k f(j, ...

  5. DP杂题2

    1.邦邦的大合唱站队 https://www.luogu.org/problem/show?pid=3694 XY说这是道简单的签到题,然后我大概是普及组都拿不到三等的那种了.. 插入题解.写得太好了 ...

  6. mysql 函数和存储过程的区别

    >一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强.存储过程,功能强大,可以执行包括修改表等一系列数据库操作:用户定义函数不能用于执行一组修改全局数据库状态的操作. > ...

  7. day16_函数作用域_匿名函数_函数式编程_map_reduce_filter_(部分)内置函数

    20180729    补充部分代码 20180727    上传代码 #!/usr/bin/env python # -*- coding:utf-8 -*- # ***************** ...

  8. latex ctex 的section不能写中文, /href

    问题描述:再使用超链接 /href 后发现section{}不能写入中文,以前是好使的,经过查询验证,需要在引导区里加入 \hypersetup{CJKbookmarks=true} 即可恢复正常.

  9. 2_1.springboot2.x配置之配置文件解析

    1.配置文件 1.Spring Boot使用一个全局的配置文件:•application.properties.application.yml 2.配置文件放在src/main/resources目录 ...

  10. 面试系列八 es写入数据的工作原理

    (1)es写数据过程 1)客户端选择一个node发送请求过去,这个node就是coordinating node(协调节点) 2)coordinating node,对document进行路由,将请求 ...