我是发了疯才来写这道题的

我如果用写这道题的时间去写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的机子好像又变慢了,这个代码好像又会被卡一个点

但是正解的线段树合并我不会写啊

不过优化一下常数就又能过了

最新文章

  1. Java版本:识别Json字符串并分隔成Map集合
  2. MVC 外网 上传 下载 实现方式(一)
  3. python之路-Day6
  4. Solve Error Debug Assertion Failed Expression vector iterators incompatible Using PCL in Release Mode of VS2010
  5. aspnetpager+repeater+oracle实现分页功能
  6. 【NOIP 2013 DAY2 T3】 华容道(spfa)
  7. 关于一个WCF调用的服务端和客户端的配置信息集合
  8. [Angular 2] Pipe Purity
  9. JavaScript 比较操作符,严格比较===
  10. RMAN完整全备份
  11. c++犯过的严重错误
  12. [置顶] perl脚本中defined,exists和delete关键字的用法和区别
  13. python 获取utc时间转化为本地时间
  14. 基于TCP协议的socket编程
  15. 01_什么是数据结构以及C语言指针回顾
  16. KVM宿主机上虚拟机动态添加新磁盘
  17. 跨域调用接口的方法之一:$.ajaxSetup()
  18. Diocp截图
  19. Four Ways to Create a Thread
  20. Codeforces Round #416 (Div. 2)A B C 水 暴力 dp

热门文章

  1. IoDH 实现的单例模式
  2. svn怎么下载代码到本地
  3. js-原始类型和声明变量
  4. bootstrap-datepicker汉化
  5. typeof()和instanceof()用法区别
  6. Linux文件系统简介----转载
  7. MFC中利用Opencv与C++抓取摄像头进行人脸识别(Mat)
  8. 制作Makefile中 ** missing separator 错误解决
  9. JavaScript中的原型和原型链
  10. 使用C++11实现完美资源管理