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> ...
随机推荐
- yum无法正常安装,提示如下 There are no enabled repos Run "yum repolist all"
一般来说著名的linux系统基本上分两大类:1 RedHat系列:Redhat.Centos.Fedora等2 Debian系列:Debian.Ubuntu等RedHat 系列:1 常见的安装包格式 ...
- final 、finalize和finally的区别
2019-04-1217:29:40 (1)final用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承.内部类要访问局部变量,局部变量必须定义成final类型,比如一段代码 (2) ...
- java.lang.UnsupportedClassVersionError: action/Login : Unsupported major.minor version 52.0 (unable to load class action.Login)异常
用myeclipse新建一个web项目,用了struts2框架,tomcat启动的时候报了这个错误. 我的问题原因是tomcat7的运行环境不知道为什么设置成了myeclipse1.7的jre,我给它 ...
- MVC 控制器中直接访问url 的方式
public void ShowDetailsImg() { //生成MD5码 string path = @"D:\其他\Test\WebApplication2\WebApplicati ...
- Effective C++ .15,16获取原始资源和成对使用同类型new和delete
15. 智能指针可以通过get操作 #include <iostream> #include <cstdlib> #include <memory> using n ...
- java 用Graphics制作模糊验证码
这篇随笔主要是java中制作验证码的效果,由于是在国庆前做的,现在也找不到原载了.我对自己整理的发表一份 生成的验证码效果如下: 一.建立一个工具类,用来生成验证码 package com.dkt.u ...
- csharp: QR Code Barcode
/// <summary> /// /// </summary> /// <param name="sender"></param> ...
- html中块级元素和行内元素
块级元素和行内元素的三个区别 1.行内元素与块级元素直观上的区别: 行内元素会在一条直线上排列,都是同一行,水平方向排列 块级元素独占一行,垂直方向排列.块级元素从新行开始结束接着一个断行 2.块级元 ...
- 08_Spring自定义标签
[ 项目工程 ] [ Person.java 模型类 ] package com.spring.selfxml.model; /** * Created by HigginCui on 2018/9/ ...
- Win7下VC++6.0打开文件报错导致其崩溃的解决办法
原文:http://blog.csdn.net/wanghaihao_1/article/details/39005771 在Windows7下安装Visual C++ 6.0后,遇到一个致命的问题打 ...