题目

题解

这道题是一道启发式合并的题目,每次合并完重构一下线段树就可以,不用线段树合并。

以操作时间为下标,建立一颗线段树,维护小球的个数与小球的颜色数,最后线段树上二分查找。

我们先不用考虑每个节点放小球数的限制,最后二分查找时,找小球数 \(\leq\) 限制数的所对应的节点下标的颜色数。

在本题中,我们要用一个 \(vector\) 来存储,通过 \(vector\) 的动态内存释放来防止 \(MLE\) 。

而且也要注意,在本题中颜色编号可能为负数,所以我们要先将其映射到正整数范围内,这个可以用 \(map\) 。

还有一个挺玄学的地方就是启发式合并,通俗得说就是由一个小集合向一个大集合合并,这样可以减少合并次数,从而减小复杂度。

这体现到本题上就是对于一个节点,如果其非叶节点,找到一个操作数最多的子树,将其它子树向这个重儿子合并。

细节挺多,且卡常的地方也很多,具体看代码

\(AC\kern 0.5emCODE:\)

Code
#include<bits/stdc++.h>
#define ri register int
#define p(i) ++i
using namespace std;
namespace IO{
char buf[1<<21],*p1=buf,*p2=buf;
#define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
inline int read() {
ri x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
return x*f;
}
}
using IO::read;//快读
namespace nanfeng{
#define cmax(x,y) ((x)>(y)?(x):(y))
#define cmin(x,y) ((x)>(y)?(y):(x))
#define FI FILE *IN
#define FO FILE *OUT
#define pb(x) push_back(x)
#define node(c,id) (node){c,id}
const int N=1e5+7;
struct edge{int v,nxt;}e[N<<1];
struct node{int c,id;};
int first[N],buc[N],tms[N],son[N],ans[N],ocu[N],t=1,tot,n,m,Q;
map<int,int> col;//可能会有负数
vector<node> ch[N];//二位数组会mle,node 可以用pair替换
inline void add(int u,int v) {
e[t].v=v;
e[t].nxt=first[u];
first[u]=t++;
}
struct Seg{//封装
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
struct Segmenttree{int sz,szc,lz;}T[N<<2];
inline void init(int x) {
T[1].sz=T[1].sz=0;
T[1].lz=1;//懒标记实在初始化时用,类似与区间更新,没用到的区间就不初始化
for (ri i(0);i<ch[x].size();p(i)) ocu[ch[x][i].c]=0;
}
inline void down(int x) {
if (!T[x].lz) return;
int l=ls(x),r=rs(x);
T[x].lz=T[l].sz=T[l].szc=T[r].sz=T[r].szc=0;
T[l].lz=T[r].lz=1;
}
inline void up(int x) {
int l=ls(x),r=rs(x);
T[x].sz=T[l].sz+T[r].sz;
T[x].szc=T[l].szc+T[r].szc;
}
void update(int x,int l,int r,int k,int c,int nm) {
if (l==r) {T[x].sz+=nm;T[x].szc+=c;return;}
down(x);
int mid=(l+r)>>1;
if (k<=mid) update(ls(x),l,mid,k,c,nm);
else update(rs(x),mid+1,r,k,c,nm);
up(x);
}
inline void merge(int x) {
for (ri i(0);i<ch[x].size();p(i)) {
node tmp=ch[x][i];
if (!ocu[tmp.c]) {
update(1,1,m,tmp.id,1,1);
ocu[tmp.c]=tmp.id;
} else if (ocu[tmp.c]>tmp.id) {
update(1,1,m,ocu[tmp.c],-1,0);
update(1,1,m,tmp.id,1,1);
ocu[tmp.c]=tmp.id;
} else update(1,1,m,tmp.id,0,1);
}
}
int query(int x,int l,int r,int k) {
if (!k) return 0;
if (l==r) return T[x].szc;
int mid=(l+r)>>1;
down(x);
int lt=ls(x),rt=rs(x);
if (T[lt].sz<=k) return T[lt].szc+query(rt,mid+1,r,k-T[lt].sz);//以小球数二分
else return query(lt,l,mid,k);
}
}T;
void dfs_init(int x,int fa) {
tms[x]=ch[x].size();
for (ri i(first[x]),v;i;i=e[i].nxt) {
if ((v=e[i].v)==fa) continue;
dfs_init(v,x);
tms[x]+=tms[v];
if (tms[v]>tms[son[x]]) son[x]=v;//这里有一个优化,因为是启发式合并,所以要先处理出操作数多的重儿子,由小向大合并,减少合并数
}
}
void dfs(int x,int fa) {
for (ri i(first[x]),v;i;i=e[i].nxt) {
if ((v=e[i].v)==fa||v==son[x]) continue;
dfs(v,x);T.init(v);//记得回溯
}
if (son[x]) dfs(son[x],x);//重儿子单独搜,不用回溯
T.merge(x);
for (ri i(first[x]),v;i;i=e[i].nxt) {
if ((v=e[i].v)==fa||v==son[x]) continue;
T.merge(v);
}
ans[x]=T.query(1,1,m,buc[x]);//二分查找
if (son[x]) {
int sx=son[x];
for (ri i(0);i<ch[x].size();p(i)) ch[sx].pb(ch[x][i]);
swap(ch[x],ch[sx]);//启发式合并
for (ri i(first[x]),v;i;i=e[i].nxt) {
if ((v=e[i].v)==fa||v==son[x]) continue;
for (ri i(0);i<ch[v].size();p(i)) ch[x].pb(ch[v][i]);
}
}
}
inline int main() {
// FI=freopen("nanfeng.in","r",stdin);
// FO=freopen("nanfeng.out","w",stdout);
n=read();
for (ri i(1);i<n;p(i)) {
int u=read(),v=read();
add(u,v);add(v,u);
}
for (ri i(1);i<=n;p(i)) buc[i]=read();
m=read();
for (ri i(1);i<=m;p(i)) {
int x=read(),c=read();
if (!col[c]) col[c]=p(tot),c=tot;//映射成整数范围
else c=col[c];
ch[x].pb(node(c,i));
}
dfs_init(1,0);dfs(1,0);
Q=read();
for (ri i(1),x;i<=Q;p(i)) printf("%d\n",ans[x=read()]);
return 0;
}
}
int main() {return nanfeng::main();}

时间复杂度为 \(\mathcal O(nlog^2n)\)?我不太会分析,哪位大佬教我一下?

NOIP 模拟 6 模板的更多相关文章

  1. NOIP模拟题汇总(加厚版)

    \(NOIP\)模拟题汇总(加厚版) T1 string 描述 有一个仅由 '0' 和 '1' 组成的字符串 \(A\),可以对其执行下列两个操作: 删除 \(A\)中的第一个字符: 若 \(A\)中 ...

  2. 「题解」NOIP模拟测试题解乱写II(36)

    毕竟考得太频繁了于是不可能每次考试都写题解.(我解释个什么劲啊又没有人看) 甚至有的题目都没有改掉.跑过来写题解一方面是总结,另一方面也是放松了. NOIP模拟测试36 T1字符 这题我完全懵逼了.就 ...

  3. 2019.7.29 NOIP模拟测试10 反思总结【T2补全】

    这次意外考得不错…但是并没有太多厉害的地方,因为我只是打满了暴力[还没去推T3] 第一题折腾了一个小时,看了看时间先去写第二题了.第二题尝试了半天还是只写了三十分的暴力,然后看到第三题是期望,本能排斥 ...

  4. noip模拟6(T2更新

    由于蒟弱目前还没调出T1和T2,所以先写T3和T4.(T1T2更完辣! update in 6.12 07:19 T3 大佬 题目描述: 他发现katarina大佬真是太强了,于是就学习了一下kata ...

  5. NOIP模拟赛20161022

    NOIP模拟赛2016-10-22 题目名 东风谷早苗 西行寺幽幽子 琪露诺 上白泽慧音 源文件 robot.cpp/c/pas spring.cpp/c/pas iceroad.cpp/c/pas ...

  6. contesthunter暑假NOIP模拟赛第一场题解

    contesthunter暑假NOIP模拟赛#1题解: 第一题:杯具大派送 水题.枚举A,B的公约数即可. #include <algorithm> #include <cmath& ...

  7. NOIP模拟赛 by hzwer

    2015年10月04日NOIP模拟赛 by hzwer    (这是小奇=> 小奇挖矿2(mining) [题目背景] 小奇飞船的钻头开启了无限耐久+精准采集模式!这次它要将原矿运到泛光之源的矿 ...

  8. 大家AK杯 灰天飞雁NOIP模拟赛题解/数据/标程

    数据 http://files.cnblogs.com/htfy/data.zip 简要题解 桌球碰撞 纯模拟,注意一开始就在袋口和v=0的情况.v和坐标可以是小数.为保险起见最好用extended/ ...

  9. 队爷的讲学计划 CH Round #59 - OrzCC杯NOIP模拟赛day1

    题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的讲学计划 题解:刚开始理解题意理解了好半天,然后发 ...

随机推荐

  1. 打开设置windows10内置linux功能-启用linux子系统

    第一步设置开发者模式 步骤:windows+s打开娜娜,输入设置,并点击. 点击更新与安全 点击开发者选项,选择开发者模型,弹出的对话框选确定之后等待安装完毕. 第二步:安装linux 点击确定后等待 ...

  2. 【故障公告】redis 服务器宕机引发博客站点故障

    非常抱歉,今天下午 17:10~17:40 左右,由于博客系统所使用的 redis 服务器宕机,造成博客站点无法正常访问,由此给您带来很大的麻烦,请您谅解. 我们会针对这次故障改进 redis 服务器 ...

  3. ES6 箭头函数你正确使用了吗

    ES6 箭头函数你正确使用了吗 博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢! 说明 在ES6中允许使用"箭头&quo ...

  4. .h .cpp区别

    首先,我们可以将所有东西都放在一个.cpp文件内. 然后编译器就将这个.cpp编译成.obj,obj是什么东西? 就是编译单元了.一个程序,可以由一个编译单元组成, 也可以有多个编译单元组成. 如果你 ...

  5. matlab——线性规划

    @ 目录 前言 一.基本概念 二.matlab实现 1.常用函数 2.常见变形 参考书目 前言 线性规划是数学规划中的一个重要分支,常用于解决如何利用现有资源来安排生产,以取得最大经济效益的问题.本文 ...

  6. [刘阳Java]_Web前端入门级练习_迅雷官宣网设计

    也有一段时间没有写一些技术专题文章,今天给大家来几篇Web前端入门级练习的文章.主讲<迅雷官宣网>的设计,相信大家平时也用过迅雷这个下载工具,所以分享的这篇技术大家在一定程序会感兴趣. 先 ...

  7. 前端之html基础演示

    1.本地服务:下载淘宝镜像node.js :https://npm.taobao.org/mirrors/npm :本次下载的版本是 v10.0.0 2.下载成功后,到cmd窗口输入 node -v, ...

  8. ThinkPHP3.2.3使用PHPExcel类操作excel导出excel

    如何导入excel请看:ThinkPHP3.2.3使用PHPExcel类操作excel导入读取excel // 引入PHPExcel类 import("Org.Util.PHPExccel& ...

  9. Linux + NodeJS 常用命令

    Linux系统常用命令 1.su 由当前用户切换至root用户: 2. su username 切换至某一用户: 3.chmod u+w /etc/sudoers 为/etc/sudoers文件添加写 ...

  10. netcore一键nssm发布为windows服务

    AntDeploy 是我开发一款开源一键部署工具包 发布功能支持: docker容器一键部署 docker镜像一键发布 支持iis一键部署 windows服务一键部署 linux服务一键部署 支持增量 ...