Regions 题解
这里提供一种时间复杂度不那么优秀但十分好写也好理解的做法。
题目大意
给定一颗 \(n\) 个节点的树,每个节点拥有一个颜色,进行若干次询问,每次询问给出两种颜色 \(A,B\),求所有颜色为 \(A\) 的节点的子树中颜色为 \(B\) 的节点的个数的和。
思路分析
考虑根号分治。按颜色的节点数进行分类,节点数 \(>\sqrt n\) 的称为重颜色,节点数 \(\le \sqrt n\) 的称为轻颜色。
对询问进行分类讨论:
- \(A,B\) 均为轻颜色:
考虑到两者的节点数都不会很多,可以暴力标记每一个颜色为 \(B\) 的点,并枚举所有颜色为 \(A\) 的点,求出其子树中的标记数并累加入答案。这一过程可以用树状数组实现。单次实现的时间复杂度为 \(O(\sqrt n\log n)\)。
- \(A\) 为重颜色,\(B\) 为轻颜色:
设 \(f_{i,j}\) 表示点 \(i\) 到根的路径中颜色为重颜色 \(j\) 的节点的数量,那么答案显然为:
\]
其中,\(\text{col}_X\) 表示所有颜色为 \(X\) 的点所构成的集合。
考虑计算 \(f\):
枚举每一个重颜色 \(K\),遍历 \(\text{col}_K\),将其中的每一个点的子树中的所有点的权值加 \(1\),再遍历每一个颜色不为 \(K\) 的点,其权值就是其对应的 \(f\)。这一过程可以用差分实现。时间复杂度为 \(O(n\sqrt n)\)。
- \(B\) 为重颜色,\(A\) 为轻颜色:
设 \(g_{i,j}\) 表示点 \(i\) 的子树中颜色为重颜色 \(j\) 的节点的数量,那么答案显然为:
\]
\(g\) 的计算与 \(f\) 正好相反:
枚举每一个重颜色 \(K\),遍历 \(\text{col}_K\),将其中的每一个点的权值单点加 \(1\)。再遍历每一个颜色不为 \(K\) 的点,其子树中的权值和即为其对应的 \(g\),这一过程可以用前缀和实现。时间复杂度为 \(O(n\sqrt n)\)。
- \(A,B\) 均为重颜色:
容易发现,这种情况是上述两种情况的交集,上述两种情况的答案计算方式均适用于此情况。
实现细节
在实现中,为了优化空间复杂度,可以省去 \(f\) 和 \(g\) 直接累加答案。具体的说,设 \(\text{ans1}_{i,j}\) 表示所有颜色为 \(i\) 的点的子树中颜色为重颜色 \(j\) 的节点的数量的和,\(\text{ans2}_{i,j}\) 表示所有颜色为 \(i\) 的点到根的路径中颜色为重颜色 \(j\) 的节点的数量的和。(也就是第三,二种情况对应的答案)这样可以将空间复杂度由 \(O(n\sqrt n)\) 优化到 \(O(R\sqrt n)\)。
差分和前缀和可以直接在 dfs 序上进行,不需要放到树上。
总时间复杂度为 \(O(q\sqrt n\log n+n\sqrt n)\),瓶颈在于树状数组,卡常后可以通过。
空间复杂度为 \(O(R\sqrt n+n)\),完全可过。
代码
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int N=200100,M=450,R=25500;
int idx=1,n,m,q,B,cnt,in1,in2,cur,ans;
int w[N],to[N],nxt[N],head[N];
int c[N],dfn[N],siz[N],big[N],d[N];
int ans1[M][R],ans2[M][R];
vector<int> col[N];
int read(){//卡常用的快读快写
int x=0;char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
void write(int x){
int k=x/10;if(k) write(k);
putchar(x-(k<<3)-(k<<1)+'0');
}
void add(int u,int v){
idx++;to[idx]=v;nxt[idx]=head[u];head[u]=idx;
}
void dfs_1(int s){
dfn[s]=++cnt;siz[s]=1;
for(int i=head[s];i;i=nxt[i])
dfs_1(to[i]),siz[s]+=siz[to[i]];
}
struct BIT{
int a[N];
#define lowbit(x) ((x)&(-(x)))
void add(int x,int v){
for(;x<N;x+=lowbit(x)) a[x]+=v;
}
int query(int x){
if(!x) return 0;
int ans=0;
for(;x;x-=lowbit(x)) ans+=a[x];
return ans;
}
}tree;
int main(){
n=read();m=read();q=read();
B=sqrt(n);
w[1]=read();
col[w[1]].push_back(1);
for(int i=2;i<=n;i++){
in1=read();w[i]=read();
add(in1,i);
col[w[i]].push_back(i);
}
dfs_1(1);
for(int i=1;i<=m;i++){
if(col[i].size()<=B) continue;
big[i]=++cur;//对每一种重颜色重标号
for(int j=1;j<=n;j++) d[j]=0;
for(auto it:col[i]) d[dfn[it]]++;//单点加
for(int j=1;j<=n;j++) d[j]+=d[j-1];//前缀和
for(int j=1;j<=n;j++){
if(w[j]==i) continue;
ans1[cur][w[j]]+=d[dfn[j]+siz[j]-1]-d[dfn[j]-1];//直接累加答案
}
for(int j=1;j<=n;j++) d[j]=0;
for(auto it:col[i]){
d[dfn[it]]++;//差分
d[dfn[it]+siz[it]]--;//这里其实是 dfn[it]+siz[it]-1+1
}
for(int j=1;j<=n;j++) d[j]+=d[j-1];//对差分序列做前缀和得到原序列
for(int j=1;j<=n;j++){
if(w[j]==i) continue;
ans2[cur][w[j]]+=d[dfn[j]];
}
}
while(q--){
in1=read();in2=read();ans=0;
if(big[in2]){write(ans1[big[in2]][in1]);cout<<'\n'<<flush;continue;}
if(big[in1]){write(ans2[big[in1]][in2]);cout<<'\n'<<flush;continue;}
for(auto it:col[in2]) tree.add(dfn[it],1);//暴力加
for(auto it:col[in1])
ans+=tree.query(dfn[it]+siz[it]-1)-tree.query(dfn[it]-1);
write(ans);cout<<'\n'<<flush;
for(auto it:col[in2]) tree.add(dfn[it],-1);//记得清空
}
return 0;
}
Regions 题解的更多相关文章
- [LeetCode]题解(python):130-Surrounded Regions
题目来源: https://leetcode.com/problems/surrounded-regions/ 题意分析: 给定给一个二维的板,这个板只包括‘X’和‘O’.将被‘X’包围的‘O’变成‘ ...
- LeetCode OJ 题解
博客搬至blog.csgrandeur.com,cnblogs不再更新. 新的题解会更新在新博客:http://blog.csgrandeur.com/2014/01/15/LeetCode-OJ-S ...
- 130. Surrounded Regions
题目: Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is capt ...
- 2014年亚洲区域赛北京赛区现场赛A,D,H,I,K题解(hdu5112,5115,5119,5220,5122)
转载请注明出处: http://www.cnblogs.com/fraud/ ——by fraud 下午在HDU上打了一下今年北京区域赛的重现,过了5题,看来单挑只能拿拿铜牌,呜呜. ...
- leetcode & lintcode 题解
刷题备忘录,for bug-free 招行面试题--求无序数组最长连续序列的长度,这里连续指的是值连续--间隔为1,并不是数值的位置连续 问题: 给出一个未排序的整数数组,找出最长的连续元素序列的长度 ...
- LeetCode All in One题解汇总(持续更新中...)
突然很想刷刷题,LeetCode是一个不错的选择,忽略了输入输出,更好的突出了算法,省去了不少时间. dalao们发现了任何错误,或是代码无法通过,或是有更好的解法,或是有任何疑问和建议的话,可以在对 ...
- USACO 6.2 Shaping Regions
Shaping Regions N opaque rectangles (1 <= N <= 1000) of various colors are placed on a white s ...
- Surrounded Regions leetcode java
题目: Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is capt ...
- (第八场)G Counting regions 【欧拉公式】
题目链接:https://www.nowcoder.com/acm/contest/146/G G.Counting regions | 时间限制:1 秒 | 内存限制:128M Niuniu lik ...
- 【leetcode刷题笔记】Surrounded Regions
Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. A region is captured ...
随机推荐
- Kubernetes(k8s) Web-UI界面(一):部署和访问仪表板(Dashboard)
目录 一.系统环境 二.前言 三.仪表板(Dashboard)简介 四.部署Kubernetes仪表板(Dashboard) 五.访问Kubernetes仪表板(Dashboard) 5.1 使用to ...
- node使用jsonwebtoken生成token与验证是否过期
场景 我们可以使用 cookie,session,token 来做鉴权. 下面我们来看一下, 如何使用 token 来做鉴权 jwt.sign 的简单介绍 npm install jsonwebtok ...
- 企业级GitLab搭建
企业级GitLab搭建 一.简介 1.GitLab概述 是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目. Rub ...
- MySQL数据库的集群方案
读写分离结构(主从) 读多写少,也就是对数据库读取数据的压力比较大. 其中一个是主库,负责写入数据,成为写库:其他都是从库,负责读取数据,成为读库. 对我们的要求: 读库和写库的数据一致: 写数据必须 ...
- CF1810D Candies题解
CF1810D Candies 点击查看原题 点击查看思路 经典的小学数学奥数题. 设 \(a\) 为每天往上爬的高度,\(b\) 为每天向下降的高度,\(n\) 为给定的需要爬上去的天数. 请注意, ...
- Spring-配置文件(引入其他配置文件,分模块开发)
引入其他配置文件 实际开发,Spring的配置文件内容非常多,这就导致了Spring配置很复杂且体积很大,所以可以将配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载 & ...
- 初识C语言中的typedef、define以及Status
小阿杰最近开始看数据结构啦嘿嘿嘿, 可惜小阿杰C语言功底稀薄,以此篇随笔记录一下我卑微的学习之路/苦涩/苦涩 首先define没啥好说的,在文件开头,定义一个固定不变的值. #define MAXN ...
- 【hack】浅浅说说自己构造hack的一些逻辑~
怎么说呢,相信很多考过竞赛的同学都会在平时的练习/考试中遭遇过100分但没有AC的情况,结果一看评测结果:subtask的数据点没过! 这时候就是遇到hack数据了,如果被这类数据卡住,说明你的代码可 ...
- Topic太多,RocketMQ炸了!
网上博客常说,kafka的topic数量过多会影响kafka,而RocketMQ不会受到topic数量影响. 但是,果真如此吗? 最近排查一个问题,发现RocketMQ稳定性同样受到topic数量影响 ...
- Unity的IUnityLinkerProcessor:深入解析与实用案例
Unity IUnityLinkerProcessor Unity IUnityLinkerProcessor是Unity引擎中的一个接口,它允许开发者在Unity项目构建时对代码进行链接处理.这个接 ...