Luogu P5168 xtq玩魔塔
这题不错啊,结合了一些不太传统的姿势。
首先看到题目有一问从一个点到另一个点边权最小值。想到了什么?
克鲁斯卡尔生成树+倍增?好吧其实有一个更常用NB的算法叫克鲁斯卡尔重构树
(不会的可以看dalao's blog,并且可以尝试切掉Luogu P4768 [NOI2018]归程)
回到这题,我们可以把重构树建出来之后直接求两点LCA的权值。
然后对于第三问,考虑继续利用重构树,我们发现此时能走到的点在树上一定是一颗子树。
子树内DFS序连续啊,所以就变成区间数颜色了,直接莫队啊!
好吧还有修改,那就带修莫队,在数据随机的情况下稳如老狗。
然后这题就完了,不过有一个细节就是克鲁斯卡尔重构树的父节点权值一定大于子节点,所以不用在向上跳的时候再维护一个最大值数组。
CODE
#include<cstdio>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RI register int
#define Tp template <typename T>
using namespace std;
const int N=100005;
struct data
{
int x,y,val;
inline friend bool operator <(data A,data B)
{
return A.val<B.val;
}
}a[N*3]; int n,m,s,opt,x,y,z,ans[N<<1],rst[N*3],dfn[N<<1],blk[N];
struct ques
{
int l,r,id,t;
inline ques (int L=0,int R=0,int Id=0,int T=0)
{
l=L; r=R; id=Id; t=T;
}
inline friend bool operator <(ques A,ques B)
{
return blk[A.l]!=blk[B.l]?blk[A.l]<blk[B.l]:(blk[A.r]!=blk[B.r]?blk[A.r]<blk[B.r]:A.t<B.t);
}
}q[N<<1]; int cnt_q,cnt_cm,cnt_col,bkt[N*3],tot,d[N<<1],sze[N<<1];
struct operation
{
int pos,col;
inline operation(int Pos=0,int Col=0) { pos=Pos; col=Col; }
}p[N<<1]; int cnt_p,col[N],now,L=1,R,list[N],ret,size;
class FileInputOutput
{
private:
static const int S=1<<21;
#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
#define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
char Fin[S],Fout[S],*A,*B; int Ftop,pt[15];
public:
Tp inline void read(T &x)
{
x=0; char ch; while (!isdigit(ch=tc()));
while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
}
Tp inline void write(T x)
{
if (!x) return (void)(pc('0'),pc('\n')); RI ptop=0;
while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
}
inline void Fend(void)
{
fwrite(Fout,1,Ftop,stdout);
}
}F;
inline void swap(int &x,int &y)
{
int t=x; x=y; y=t;
}
class Double_Increased_On_Tree
{
private:
static const int P=18;
struct edge
{
int to,nxt;
}e[N<<1]; int head[N<<1],cnt,idx,anc[N<<1][P],dep[N<<1];
inline void reset(int now)
{
for (RI i=0;i<P-1;++i) if (anc[now][i])
anc[now][i+1]=anc[anc[now][i]][i]; else break;
}
inline void miner(int &x,int y)
{
if (y<x) x=y;
}
public:
inline void add(int x,int y)
{
e[++cnt]=(edge){y,head[x]},head[x]=cnt;
}
#define to e[i].to
inline void DFS(int now)
{
if (now<=n) list[dfn[now]=++idx]=now,sze[now]=1;
else dfn[now]=1e9;reset(now); for (RI i=head[now];i;i=e[i].nxt)
anc[to][0]=now,dep[to]=dep[now]+1,DFS(to),sze[now]+=sze[to],miner(dfn[now],dfn[to]);
}
#undef to
inline int getLCA(int x,int y)
{
RI i; if (dep[x]<dep[y]) swap(x,y); for (i=P-1;~i;--i)
if (dep[anc[x][i]]>=dep[y]) x=anc[x][i]; if (x==y) return x;
for (i=P-1;~i;--i) if (anc[x][i]!=anc[y][i])
x=anc[x][i],y=anc[y][i]; return anc[x][0];
}
inline int getinterval(int x,int y)
{
for (RI i=P-1;~i;--i)if (anc[x][i]&&d[anc[x][i]]<=y) x=anc[x][i]; return x;
}
}T;
class Kruskal_Rubuild_Tree_Solver
{
private:
int father[N<<1];
inline int getfather(int x)
{
return father[x]^x?father[x]=getfather(father[x]):x;
}
public:
inline void init(void)
{
for (RI i=1;i<=n;++i) father[i]=i;
}
inline void Kruskal(void)
{
sort(a+1,a+m+1); init(); for (RI i=1;i<=m;++i)
if ((a[i].x=getfather(a[i].x))!=(a[i].y=getfather(a[i].y)))
{
d[++tot]=a[i].val; father[a[i].x]=father[a[i].y]=tot;
T.add(tot,a[i].x); T.add(tot,a[i].y); father[tot]=tot;
}
}
}K;
inline void add(int col)
{
if (++bkt[col]==1) ++ret;
}
inline void del(int col)
{
if (--bkt[col]==0) --ret;
}
inline void travel(int now)
{
if (p[now].pos>=L&&p[now].pos<=R) del(col[list[p[now].pos]]),
add(p[now].col); swap(p[now].col,col[list[p[now].pos]]);
}
int main()
{
//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
RI i; for (F.read(n),F.read(m),F.read(s),i=1;i<=n;++i)
F.read(col[i]),rst[++cnt_col]=col[i]; for (i=1;i<=m;++i)
F.read(a[i].x),F.read(a[i].y),F.read(a[i].val);
for (tot=n,K.Kruskal(),T.DFS(tot),i=1;i<=s;++i)
{
F.read(opt); F.read(x); F.read(y);
switch (opt)
{
case 1:
p[++cnt_p]=operation(dfn[x],y); rst[++cnt_col]=y; break;
case 2:
ans[++cnt_q]=d[T.getLCA(x,y)]; break;
case 3:
int top=T.getinterval(x,y); q[++cnt_cm]=ques(dfn[top],dfn[top]+sze[top]-1,++cnt_q,cnt_p); break;
}
}
sort(rst+1,rst+cnt_col+1); cnt_col=unique(rst+1,rst+cnt_col+1)-rst-1;
for (i=1;i<=n;++i) col[i]=lower_bound(rst+1,rst+cnt_col+1,col[i])-rst;
for (i=1;i<=cnt_p;++i) p[i].col=lower_bound(rst+1,rst+cnt_col+1,p[i].col)-rst;
for (size=(int)pow(n,2.0/3.0),i=1;i<=n;++i) blk[i]=(i-1)/size+1;
for (sort(q+1,q+cnt_cm+1),i=1;i<=cnt_cm;++i)
{
while (now<q[i].t) travel(++now); while (now>q[i].t) travel(now--);
while (L>q[i].l) add(col[list[--L]]); while (R<q[i].r) add(col[list[++R]]);
while (L<q[i].l) del(col[list[L++]]); while (R>q[i].r) del(col[list[R--]]);
ans[q[i].id]=ret;
}
for (i=1;i<=cnt_q;++i) F.write(ans[i]); return F.Fend(),0;
}
Luogu P5168 xtq玩魔塔的更多相关文章
- P5168 xtq玩魔塔 [克鲁斯卡尔重构树+带修莫队]
P5168 xtq玩魔塔 又是码农题- 利用克鲁斯卡尔重构树的性质 我们就可以得出 \(dep\) 值小的,肯定比 \(dep\) 大的值要优. 于是第二问就可以直接 LCA 求出来了- 至于第三问, ...
- P5168 xtq玩魔塔
传送门 其实就是板子--只要会克鲁斯卡尔重构树和带修莫队就可以了 这么想着的我就调了将近一个下午-- 思路其实比较清晰,然而码量很大,细节贼多-- 不难看出只在最小生成树上走最优,于是建出克鲁斯卡尔重 ...
- 【Luogu P5168】xtq玩魔塔(Kruskal 重构树 & 树状数组 & set)
Description 给定一个 \(n\) 个顶点,\(m\) 条边的无向联通图,点.边带权. 先有 \(q\) 次修改或询问,每个指令形如 \(\text{opt}\ x\ y\): \(\tex ...
- SDUTOJ2465:其实玩游戏也得学程序(bfs+优先队列+回溯)
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2465 题目描述 由于前两次的打击,ZYJ同学不 ...
- 这款打怪升级的小游戏,7 年前出生于 GitHub 社区,如今在谷歌商店有 8 万人打了满分
今天我在 GitHub 摸鱼寻找新的"目标"时,发现了一个开源项目是 RougeLike 类的角色扮演游戏「破碎版像素地牢」(Shattered Pixel Dungeon)类似魔 ...
- Luogu P4705 玩游戏
题目描述 Alice 和 Bob 又在玩游戏. 对于一次游戏,首先 Alice 获得一个长度为 的序列 ,Bob 获得一个长度为 的序列 bb.之后他们各从自己的序列里随机取出一个数,分别设 ...
- Luogu 4705 玩游戏
看见这个题依稀想起了$5$月月赛时候的事情,到现在仍然它感觉非常神仙. 游戏$k$次价值的期望答案 $$ans_k = \frac{1}{nm}\sum_{i = 1}^{n}\sum_{j = 1} ...
- Luogu 4251 [SCOI2015]小凸玩矩阵
BZOJ 4443 二分答案 + 二分图匹配 外层二分一个最小值,然后检验是否能选出$n - k + 1$个不小于当前二分出的$mid$的数.对于每一个$a_{i, j} \geq mid$,从$i$ ...
- [LUOGU] P4251 [SCOI2015]小凸玩矩阵
行列看成点,格子看成边,二分一个边权,删去大于它的边,新图上的最大流>k则答案可以更优,小于k则调整左边界. #include<algorithm> #include<iost ...
随机推荐
- 计算机网络TCP“三次握手”
终于有时间写这篇文章了,最近真的比较忙! TCP协议 之 “三次握手” 引言:我们知道,TCP是面向连接的协议(相较于UDP无连接的协议),会在传送数据之前先在 发送端 & 接收端 之间建立 ...
- CSS回顾(常见问题解决)
一.margin的塌陷解决: BFC (block format context)块级格式化上下文格式 display:inline-block float:left / right overflow ...
- Mac 电脑如何安装mac os 和win7双系统(win7多分区)
转载:Mac 电脑如何安装mac os 和win7双系统(win7多分区) 本文主要参考了2篇博文,并通过自己的亲身实践总结的.参考的2篇博文地址: http://wenku.baidu.com/li ...
- 关于正餐智能POS6.0.1.1改版后,订单模块无法进行部分退款的FAQ
适用版本:智能POS正餐V6.0.1.1+ 适用情况:订单模块,无法输入自定义金额进行部分退款. 原因:为让报表统计的数据更准确. 改版之后仍可适用部分退款的情况: 1.口碑先付订单,可在口碑模块,选 ...
- [20190321]smem的显示缺陷.txt
[20190321]smem的显示缺陷.txt1.smem 加入-m参数显示存在缺陷,map的信息不全:# smem -tk -m -U oracle -P "oraclepeis|ora_ ...
- 洗礼灵魂,修炼python(63)--爬虫篇—re模块/正则表达式(1)
爬虫篇前面的某一章了,我们要爬取网站页面源代码的数据,要从中获取到我们想要的数据,是不是感觉很费力,确实费力对吧?那么有没有什么有利的工具来解决这个问题呢?那就是这一篇博文的主题—— 正则表达式简介 ...
- memset memcmp memcpy memmove 自己实现
memset memcmp memcpy memmove 自己实现 memset #include <stdio.h> #include <memory.h> #include ...
- 为爱好舞蹈的人们做的软件,细究数据结构,操作系统,磁盘原理,用java/c/c++写一个开源 MP3助手
1.可以给歌曲间插播空白音乐 2.拖拽式调整 3.先排序,后一键写入顺序文件. 国外的开源软件 MP3 播放排序 http://www.murraymoffatt.com/software-prob ...
- 第10章 嵌入式Linux 的调试技术
10.1 打印内核调试信息:printk printk位函数运行在内核空间, printf函数运行在用户空间.也就是说,像Linux 驱动这样的Linux内核程序只能使用printk函数输出调试信息 ...
- Java入门(二):注释和基本数据类型
上次通过eclipse在控制台输出了hello world,是不是有点小激动啊,今天接着介绍Java基础知识. 一.Java注释 1.Java注释语句不会被编译器运行,不用担心代码因为许多注释语句显得 ...