JZOJ4811【排队】

JZOJ4811【排队】_第1张图片JZOJ4811【排队】_第2张图片

【题目大意】

对于给定的一棵树(初始全为白点),执行两种询问

1.每次询问给这棵树填充x个黑点,填充规则:每个黑点从根节点(1)往叶子节点走(只能走白点)(如果有多个可行路径,则走到比编号最小的子节点),直到不能走为止,最后停留的位置被染为黑色,并输出第x个黑点最后停留的位置

2.将x位置的黑点染为白色,其余黑点按照1规则重新填充,问有多少个黑点改变了位置

题目保证询问合法

【题解】

首先询问2应该比较好想到,每个黑点只会影响到是他的祖先的黑点(自行yy),所以只需树上倍增,每次询问找到x往上走最后一个黑点祖先j,ans=d[j]-d[x](d为深度)并把j染为白色

现在思考询问1,对于询问1不难想到,每个点都有自己的优先级(按照规则自行yy),一个点要填充时当且仅当所有优先级比它低的点都已被填充,所以就可以用一个堆维护剩余未填充点的优先级,每次填充堆顶的节点

当然我是用一个比较迷幻的dfs,不难想到,若要填充一个节点,当且仅当他的所有儿子节点都被填满,以及他的编号比他小的兄弟都被填满,所以就可以用一个dfs实现填充(填充前先给每个点连出的边排序,为了使得编号小的儿子节点被更早访问),比较zz的填充方法就是每次从节点1往下dfs遇到不能染色的点就不进入,当一个点所有的儿子节点都填满时在给他染色(我最后就是这样打,虽然我打的那个是我接下来要说的正确的dfs),不难想到,这个方法随便出个数据就可以卡掉了(会超时啊233)(虽然并没存在这种数据),然后在深入往下思考,可以dfs时会把许多一万年都不会被染色的点遍历一万次(比如根节点),所以我们可以改变一下dfs的顺序,每次从优先级最高的白点开始dfs,然后进行普通的dfs,再加上一个模拟回溯的过程,就是记录当前这个节点是不是由它的父亲走过来的(因为我们dfs的第一个节点并不是根节点),如果不是(比如他可能从它的儿子走过来)就dfs它的父亲节点(这里就是从儿子走到父亲),这样就可以实现一个模拟回溯的过程。

总之讲的有点迷幻(我也觉得好迷幻)

详见代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
int i,j,k,l,m,n,t,s,x,y;
vector  road[100005];
int f[100005][22],col[100005],d[100005],child[100005];
void build(int u)
  {
  	int i,v;
  	for (i=0;i


你可能感兴趣的:(倍增,搜索)