题面: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. PHP跨服务器提交数据

    关于类似的问题,百度上一搜一大堆,这只是我自己实际用过的两个方法,不多BB直接上代码 1.第一种: 2.第二种 可以随意切换POST和GET提交方式

  2. 【转载】Abstract Factory Step by Step --- 抽象工厂

    抽象工厂是创建型模式的代表,其他的还有单件(Singleton).生成器(Builder).工厂方法(Factory Method)以及原型(Prototype),模式本身没有好坏之分,只有适用不适用 ...

  3. thinkphp 模型定义

    模型定义 模型类并非必须定义,只有当存在独立的业务逻辑或者属性的时候才需要定义. 模型类通常需要继承系统的\Think\Model类或其子类,下面是一个Home\Model\UserModel类的定义 ...

  4. thinkphp 判断请求类型

    判断请求类型 在很多情况下面,我们需要判断当前操作的请求类型是GET .POST .PUT或 DELETE,一方面可以针对请求类型作出不同的逻辑处理,另外一方面有些情况下面需要验证安全性,过滤不安全的 ...

  5. luoguP2580 于是他错误的点名开始了 [Trie]

    题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛CON900). ...

  6. python处理多线程之间事件通讯方法

    一.什么是事件 每执行一个事情,肯定有该事情的执行后状态,那事件就是该事情发生的信号 在程序中,多线程之间需要通讯,而事件就是方便线程之间的通讯 案例: 1.服务器启动需要5秒 2.客服端启动后去链接 ...

  7. JSON对象和字符串之间的相互转换 – JSON.parse() 和 JSON.stringify()

    所有现代浏览器都支持 JSON 对象,有两个非常有用的方法来处理 JSON 格式的内容: JSON.parse(string) :接受一个 JSON 字符串并将其转换成一个 JavaScript 对象 ...

  8. ASP.NET自定义Validform的datatype

    1.定义 <script type="text/javascript"> $(function () { $("#aa").Validform({ ...

  9. poj-3468-A Simple Problem with Integers-线段树入门+区间更新

    You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op ...

  10. 面试系列 30 如何自己设计一个类似dubbo的rpc框架

    其实一般问到你这问题,你起码不能认怂,因为既然咱们这个课程是短期的面试突击训练课程,那我不可能给你深入讲解什么kafka源码剖析,dubbo源码剖析,何况我就算讲了,你要真的消化理解和吸收,起码个把月 ...