newcoder NOIP提高组模拟赛C题——保护
我是发了疯才来写这道题的
我如果用写这道题的时间去写dp,我估计我能写上三四道
可怕的数据结构题
这道题的鬼畜之处在于实在是不太好写
我们看到要求离树根尽量的近,所以我们很容易就能想到树上倍增,所以我们需要有一种能快速求出一条路径能被多少条给出路径完全覆盖
我们知道起点是固定的,要求完全覆盖的话我们必须要保证给定的路径的一个端点在起点的子树里,同时还要求另一个端点在路径的终点的外部,也就是说路径的\(LCA\)深度小于等于终点
于是这样就可以写一个还算可观的\(40\)分暴力了
这是考场上想出主席树没敢打的40分暴力,核心思想就是在起点的子树里找路径的端点,之后判断这些端点所对应的路径的\(LCA\)的深度
复杂度是\(O(qnlogn)\)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#define max(a,b) ((a)>(b)?(a):(b))
#define re register
#define maxn 50005
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
	  x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
int head[maxn],deep[maxn],fa[maxn],maxdep=1;
std::vector<int> v[maxn];
struct node
{
	int v,nxt;
}e[maxn<<1];
int num,H;
inline void add_edge(int x,int y)
{
	e[++num].v=y;
	e[num].nxt=head[x];
	head[x]=num;
}
int lca[maxn];
int f[maxn][20];
int tot,n,m,X[maxn],Y[maxn];
void dfs(int x)
{
	for(re int i=head[x];i;i=e[i].nxt)
	if(!deep[e[i].v])
	{
		f[e[i].v][0]=x;
		deep[e[i].v]=deep[x]+1;
		dfs(e[i].v);
	}
}
inline int LCA(int x,int y)
{
	if(deep[x]<deep[y]) std::swap(x,y);
	for(re int i=H;i>=0;i--)
	if(deep[f[x][i]]>=deep[y]) x=f[x][i];
	if(x==y) return x;
	for(re int i=H;i>=0;i--)
	if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}
inline int check(int x,int now)
{
	int ans=0;
	for(re int i=0;i<v[x].size();i++) if(deep[v[x][i]]<=now) ans++;
	for(re int i=head[x];i;i=e[i].nxt)
	if(deep[e[i].v]>deep[x]) ans+=check(e[i].v,now);
	return ans;
}
int main()
{
	n=read(),m=read();
	int x,y;
	for(re int i=1;i<n;i++)
	{
		x=read(),y=read();
		add_edge(x,y),add_edge(y,x);
	}
	deep[1]=1;
	dfs(1);
	H=log2(n);
	for(re int i=1;i<=H;i++)
		for(re int j=1;j<=n;j++)
			f[j][i]=f[f[j][i-1]][i-1];
	for(re int i=1;i<=m;i++)
	{
		X[i]=read(),Y[i]=read();
		if(deep[X[i]]>deep[Y[i]]) std::swap(X[i],Y[i]);
		lca[i]=LCA(X[i],Y[i]);
		v[Y[i]].push_back(lca[i]);
		if(X[i]!=Y[i]) v[X[i]].push_back(lca[i]);
	}
	int Q=read(),k;
	while(Q--)
	{
		x=read(),k=read();
		int sx=x;
		for(re int i=H;i>=0;i--)
			if(f[x][i]&&check(sx,deep[f[x][i]])>=k) x=f[x][i];
		printf("%d\n",deep[sx]-deep[x]);
	}
	return 0;
}
这根正解其实很接近了,我们想要快速判断一个答案是否可行的话,我们可以利用主席树来做
这里的主席树需要解决子树内的问题,所以还是按照\(dfs\)序来建主席树,之后主席树里的权值是这个路径端点对应的\(LCA\)的深度
之后我们就可以利用主席树差分知道一个子树内的所有端点对应的\(LCA\)的深度小于等于某个值得有多少个了
所以就可以倍增加上主席树判断,时间复杂度\(O(nlogn+qlog^2n)\)
主要是太难写了,考场上想出正解也不敢写
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#define re register
#define maxn 200005
#define max(a,b) ((a)>(b)?(a):(b))
inline int read()
{
	char c=getchar();
	int x=0;
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9')
	  x=(x<<3)+(x<<1)+c-48,c=getchar();
	return x;
}
int head[maxn],deep[maxn],maxdep=1;
int sum1[maxn],X[maxn],Y[maxn];
struct node
{
	int v,nxt;
}e[maxn<<1];
int num;
inline void add_edge(int x,int y)
{
	e[++num].v=y;
	e[num].nxt=head[x];
	head[x]=num;
}
std::vector<int> v[maxn];
int sum[maxn],to[maxn],_to[maxn];
//_to[i]是将序列上的点i映射到序列上,to[i]是将树上的点映射到序列上
int top[maxn],son[maxn],lca[maxn];
int f[maxn][19];
int tot;
void dfs(int x)
{
	_to[++tot]=x;
	to[x]=tot;
	int maxx=-1;
	sum1[x]=1;
	for(re int i=head[x];i;i=e[i].nxt)
	if(!deep[e[i].v])
	{
		f[e[i].v][0]=x;
		deep[e[i].v]=deep[x]+1;
		maxdep=max(maxdep,deep[e[i].v]);
		dfs(e[i].v);
		sum1[x]+=sum1[e[i].v];
		if(sum1[e[i].v]>maxx) maxx=sum1[e[i].v],son[x]=e[i].v;
	}
}
void dfs2(int x,int topf)
{
	top[x]=topf;
	if(!son[x]) return;
	dfs2(son[x],topf);
	for(re int i=head[x];i;i=e[i].nxt)
	if(deep[e[i].v]>deep[x]&&e[i].v!=son[x]) dfs2(e[i].v,e[i].v);
}
inline int LCA(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(deep[top[x]]<deep[top[y]]) std::swap(x,y);
		x=f[top[x]][0];
	}
	if(deep[x]>deep[y]) return y;
	return x;
}
int l[maxn<<6],r[maxn<<6],d[maxn<<6];
int rt[maxn];
int n,m,cnt;
int Build(int x,int y)
{
	int root=++cnt;
	int mid=x+y>>1;
	if(x==y) return root;
	l[root]=Build(x,mid);
	r[root]=Build(mid+1,y);
	return root;
}
int change(int pre,int x,int y,int t)
{
	int root=++cnt;
	d[root]=d[pre]+1;
	if(x==y) return root;
	l[root]=l[pre];
	r[root]=r[pre];
	int mid=x+y>>1;
	if(t<=mid) l[root]=change(l[pre],x,mid,t);
	else r[root]=change(r[pre],mid+1,y,t);
	return root;
}
inline int query(int pre,int x,int y,int xx,int yy)
{
	if(xx<=x&&yy>=y) return d[pre];
	int mid=x+y>>1;
	if(yy<=mid) return query(l[pre],x,mid,xx,yy);
	if(xx>mid) return query(r[pre],mid+1,y,xx,yy);
	return  query(l[pre],x,mid,xx,yy)+query(r[pre],mid+1,y,xx,yy);
}//主席树的板子
int main()
{
	n=read(),m=read();
	int x,y;
	for(re int i=1;i<n;i++)
	{
		x=read(),y=read();
		add_edge(x,y),add_edge(y,x);
	}
	deep[1]=1;
	dfs(1);
	int H=log2(maxdep)+1;
	dfs2(1,1);
	for(re int i=1;i<=H;i++)
		for(re int j=1;j<=n;j++)
			f[j][i]=f[f[j][i-1]][i-1];
	for(re int i=1;i<=m;i++)
	{
		X[i]=read(),Y[i]=read();
		lca[i]=LCA(X[i],Y[i]);
		v[Y[i]].push_back(lca[i]),sum[Y[i]]++;
		if(X[i]!=Y[i]) v[X[i]].push_back(lca[i]),sum[X[i]]++;
        //一条路径正反算两次,如果是同一个点就只算一次
	}
	rt[0]=Build(1,maxdep);
	int pre=0;
	for(re int i=1;i<=n;i++)
	{
		if(!sum[_to[i]])
		{
			rt[i]=rt[i-1];
			continue;
		}
		int T=rt[i-1];
		for(re int j=0;j<v[_to[i]].size();j++)
			T=change(T,1,maxdep,deep[v[_to[i]][j]]);
        //一个点可能是多条路径的端点
		rt[i]=T;
	}
	int Q=read(),k;
	while(Q--)
	{
		x=read(),k=read();
		int sx=x;
		for(re int i=H;i>=0;i--)
		{
			if(!f[x][i]) continue;
 			int mid=query(rt[to[sx]+sum1[sx]-1],1,maxdep,1,deep[f[x][i]]);
			int MID=-query(rt[to[sx]-1],1,maxdep,1,deep[f[x][i]]);
			if(f[x][i]&&mid+MID>=k)
			x=f[x][i];
		}
		printf("%d\n",deep[sx]-deep[x]);
	}
	return 0;
}
newcoder的机子好像又变慢了,这个代码好像又会被卡一个点
但是正解的线段树合并我不会写啊
不过优化一下常数就又能过了
newcoder NOIP提高组模拟赛C题——保护的更多相关文章
- 10-18 noip提高组模拟赛(codecomb)T1倍增[未填]
		
T1只想到了找环,> <倍增的思想没有学过,所以看题解看得雨里雾里的(最近真的打算学一下! 题目出的挺好的,觉得noip极有可能出现T1T2T3,所以在此mark 刚开始T1以为是模拟,还 ...
 - HGOI20180815 (NOIP 提高组模拟赛 day2)
		
Day 2 rank 11 100+35+30=165 本题是一道数论题,求ax+by=c的正整数对(x,y) x>=0并且y>=0 先说下gcd: 求a,b公约数gcd(a,b) 如gc ...
 - 【洛谷】NOIP提高组模拟赛Day2【动态开节点/树状数组】【双头链表模拟】
		
U41571 Agent2 题目背景 炎炎夏日还没有过去,Agent们没有一个想出去外面搞事情的.每当ENLIGHTENED总部组织活动时,人人都说有空,结果到了活动日,却一个接着一个咕咕咕了.只有不 ...
 - [LUOGU]  NOIP提高组模拟赛Day1
		
题外话:以Ingress为题材出的比赛好评,绿军好评 T1 考虑枚举第\(i\)个人作为左边必选的一个人,那左边剩余\(i-1\)个人,选法就是\(2^{i-1}\),也就是可以任意选或不选,右侧剩余 ...
 - 10-18 noip提高组模拟赛(codecomb)T2贪心
		
T2:找min:一直找最小的那个,直到a[i]-x+1小于0,就找次小的,以此类推: 求max,也是一样的,一直到最大的那个,直到次大的比之前最大的大,就找次大的: 这个模拟,可以用上priority ...
 - 计蒜客 2017 NOIP 提高组模拟赛(四)Day1 T2 小X的密室
		
https://nanti.jisuanke.com/t/17323 小 X 正困在一个密室里,他希望尽快逃出密室. 密室中有 N 个房间,初始时,小 X 在 1号房间,而出口在 N号房间. 密室的每 ...
 - 【洛谷】NOIP提高组模拟赛Day1【组合数学】【贪心+背包】【网络流判断是否满流以及流量方案】
		
U41568 Agent1 题目背景 2018年11月17日,中国香港将会迎来一场XM大战,是世界各地的ENLIGHTENED与RESISTANCE开战的地点,某地 的ENLIGHTENED总部也想派 ...
 - noip提高组模拟赛(QBXT)T2
		
T2count题解 [ 问题描述]: 小 A 是一名热衷于优化各种算法的 OIER,有一天他给了你一个随机生成的 1~n 的排列, 并定 义区间[l,r]的价值为: \[ \huge C_{l,r}= ...
 - l洛谷 NOIP提高组模拟赛 Day2
		
传送门 ## T1 区间修改+单点查询.差分树状数组. #include<iostream> #include<cstdio> #include<cstring> ...
 
随机推荐
- Golang教程:常量
			
定义常量 常量(constant)表示固定的值,比如:5,-89,"I love Go",67.89 等等. 考虑如下程序: var b string = "I love ...
 - SIMD
			
SIMD 概述 数据类型 静态方法:数学运算 静态方法:通道处理 静态方法:比较运算 静态方法:位运算 静态方法:数据类型转换 实例方法 实例:求平均值 概述 SIMD(发音/sim-dee/)是“S ...
 - 第七章使用java实现面向对象- 多线程
			
一.Thread类和Runnable接口 1.在java.lang包中定义了Runnable接口和Thread类. Runnable接口中只定义了一个方法,它的格式为: public abstract ...
 - No.2一步步学习vuejs 实例demo篇
			
简单应用Vue.js 的核心是一个允许采用简洁的模板语法来声明式的将数据渲染进 DOM 的系统: <div id="app"> {{ message }} </d ...
 - Windbg  脚本命令简介 一
			
Windbg 脚本命令简介 一 Windbg command r: registers的简写,可以显示或修改寄存器的值.浮点寄存器的值.定义别名变量. 可以显示当前线程下的寄存器值. The r c ...
 - Sqoop迁移Hadoop与RDBMS间的数据
			
Sqoop是用来实现结构型数据(如:关系型数据库RDBMS)和Hadoop之间进行数据迁移的工具.它充分利用了MapReduce的并行特点以批处理的方式加快数据的传输,同时也借助MapReduce实现 ...
 - Sencha Architect打开闪退问题修复
			
删除以下位置的cache文件夹 C:\Users\Administrator\AppData\Local\Sencha\Sencha Architect 3.2\Cache bug解决参考 https ...
 - typeof的探讨
			
console.log(typeof 'abc') // "string" console.log(typeof true )// "boolean" cons ...
 - localstorage本地存储的简单使用
			
我们在做页面时会用到本地存储的时候,今天说说localStorage本地存储. 1.localStorage.name="老王"; //第一种设置存储本地数据的方法loc ...
 - chrome 控制台里  打印对象
			
我们经常使用 chrome 的 控制台 console.log() 打印 但有时候我们需要把一个对象复制下来(而这个对象嵌套比较深) 打印出来的我们不好复制 如下图 我们可以使用谷歌控制台的c ...