CSP-S模拟41影子,玫瑰花精题解
题面: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影子,玫瑰花精题解的更多相关文章
- csp-s模拟测试41「夜莺与玫瑰·玫瑰花精·影子」
夜莺与玫瑰 题解 联赛$T1$莫比乌斯$\%\%\%$ $dead$ $line$是直线 首先横竖就是$n+m$这比较显然 枚举方向向量 首先我们枚举方向向量时只枚举右下方向,显然贡献$*2$就是所 ...
- NOIP 模拟 $15\; \rm \text{玫瑰花精}$
题解 \(by\;zj\varphi\) 一道线段树题目 这道题可以通过维护一棵线段树,线段树上的每个节点维护 \(\rm l,r,len,p\) 分别表示这段区间最左边的花精,最右边的花精,被两只花 ...
- [CSP模拟测试43、44]题解
状态极差的两场.感觉现在自己的思维方式很是有问题. (但愿今天考试开始的一刻我不会看到H I J) A 考场上打了最短路+贪心,水了60. 然而正解其实比那30分贪心好想多了. 进行n次乘法后的结果一 ...
- [CSP-S模拟测试]:影子(并查集+LCA)
题目描述 一个人有很多的影子,新的旧的,他们不断消失重来.学者的影子在他苍白色的精神图景里成为了$n$个黑色的点,他们伸长的触手交叉形成了一颗黑色的树.假使每个影子点拥有一个权值$d_i$,黑色的树边 ...
- 蓝桥杯大学B组省赛2020模拟赛(一)题解与总结
题目链接:https://www.jisuanke.com/contest/6516 A:题目: 我们称一个数是质数,而且数位中出现了 5 的数字是有趣的. 例如 5, 59, 457.求1到1000 ...
- WC2019 全国模拟赛第一场 T1 题解
由于只会T1,没法写游记,只好来写题解了... 题目链接 题目大意 给你一个数列,每次可以任取两个不相交的区间,取一次的贡献是这两个区间里所有数的最小值,求所有取法的贡献和,对 \(10^9+7\) ...
- NOIP2017金秋冲刺训练营杯联赛模拟大奖赛第一轮Day2题解
上星期打的...题有点水,好多人都AK了 T1排个序贪心就好了 #include<iostream> #include<cstring> #include<cstdlib ...
- CSPS模拟 41
说不会鸽就不会鸽的 虽然是炸裂的一场 T1没读懂题,T23交了两个无脑暴力 (公式懒得打了 latex过于感人) T1 点阵内不重合的直线有多少条? 枚举斜率,那么“后继”不在点阵内的点可以作出一个贡 ...
- [7.18NOIP模拟测试5]砍树 题解(数论分块)
题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...
随机推荐
- 基于vue的环信基本实时通信功能
本篇文章借鉴了一些资料,然后在这个基础上,我将环信的实现全部都集成在一个组件里面进行实现: https://blog.csdn.net/github_35631540/article/details/ ...
- 二分图建图,并查集求联通——二维等价性传递 cf1012B好题!
/* 模拟二分图:每个点作为一条边,连接的是一列和一行(抽象成一个点,列在左,行在右) 由题意得 a-b相连,a-c相连,b-d相连,那么d-c就不用再相连了 等价于把二分图变成联通的需要再加多少边 ...
- Arcmap中处理面图层中空白碎片
在面数据中,有时候在一个面要素中会出现碎片,而我们大多时候不希望这些碎片存在(图1),下面介绍通过Editor工具把这些碎片处理掉.
- Pascal 排序算法
Pascal 排序 排序 排序就是将杂乱无章的数据元素,通过一定的方法按关键字顺序排列的过程.排序问题是一个十分重要的问题,并且排序的方法有很多种: 例子:输入20个数,将它们按照从高到低的次序排 ...
- maven项目引入外部第三方jar包,引入、本地编译、第三方jar一起打到jar中、在linux机器中解决classnotfound(配置classpath),笔记整理。
文章目录 引用的第三方jar的目录结构(示例) 引入第三方jar进行dependency使项目内能import 本地编译 第三方jar一起打到jar中 在linux机器中解决classnotfound ...
- Android Butterknife使用方法总结
原文链接:http://blog.csdn.net/donkor_/article/details/77879630 前言: ButterKnife是一个专注于Android系统的View注入框架,以 ...
- 深入分析Service启动、绑定过程
Service是Android中一个重要的组件,它没有用户界面,可以运行在后太做一些耗时操作.Service可以被其他组件启动,甚至当用户切换到其他应用时,它仍然可以在后台保存运行.Service 是 ...
- IDEA 安装lombok及使用
1.File-Settings-Plugins-Brows Repositories-输入lombok-install 2.重启idea 3.添加maven依赖 <dependency> ...
- java_缓冲流(文件内容排序)
/** 案例:诸葛亮出师表文本排序 * 1.使用HashMap集合,k存储每行文本序,v存储文本 * 2.创建字符缓冲输入流,构造方法中绑定字符输入流 * 3.使用字符串缓冲输入流中的方法readLi ...
- JS规则 我或你都可以 (逻辑或操作符)||逻辑或操作符,相当于生活中的“或者”,当两个条件中有任一个条件满足,“逻辑或”的运算结果就为“真”
我或你都可以 (逻辑或操作符) "||"逻辑或操作符,相当于生活中的"或者",当两个条件中有任一个条件满足,"逻辑或"的运算结果就为&quo ...