zoukankan      html  css  js  c++  java
  • #10471. 「2020-10-02 提高模拟赛」灌溉 (water)

    题面:#10471. 「2020-10-02 提高模拟赛」灌溉 (water)

    假设只有一组询问,我们可以用二分求解:二分最大距离是多少,然后找到深度最大的结点,并且把它的(k)倍祖先的一整子树删掉,看一下一共要删几次,显然满足单调性。

    现在要询问所有取值。上面二分的过程启发我们可以反过来,通过枚举答案,然后找到答案对应哪些询问。显然对于当前( ext{ans}),一次删除最少删掉(ans+1)个点,最多删(frac{n}{ans+1})次,因此是一个调和级数(frac{1}{1}+frac{1}{2}+frac{1}{3}+frac{1}{4}+....+frac{1}{n} -> nln(n)),只要保证每次枚举的时间复杂度与(n)脱钩就行,用线段树维护剩余数的最大值的位置就行了,时间复杂度(nlog^2(n))

    这个线段树的实现比较巧妙,( ext{lazytag})有三种取值,(0)表示没有操作,(1)表示要把孩子们全删了,(2)表示要把孩子们全弄回来。若一个点的懒标记大于零,就直接用它的懒标记覆盖它儿子们的懒标记。为了实现恢复操作,要额外存下每个点最初始的值用作恢复用。

    
    int tree[maxn * 4], val[maxn * 4], fir[maxn * 4];
    
    /// val: 0 -> 无要求; 1 -> 要求填满; 2 -> 要求删掉
    
    int pushup(int x, int y)
    {
    	return dep[x] < dep[y] ? y : x;
    }
    
    void spread(int x)
    {
    	if (val[x] == 1)
    	{
    		val[x << 1] = val[x << 1 | 1] = 1;
    		tree[x << 1] = fir[x << 1];
    		tree[x << 1 | 1] = fir[x << 1 | 1];
    	}
    	if (val[x] == 2)
    	{
    		val[x << 1] = val[x << 1 | 1] = 2;
    		tree[x << 1] = tree[x << 1 | 1] = 0;
    	}
    	val[x] = 0;
    }
    
    void build(int x, int l, int r)
    {
    	val[x] = true;
    	if (l == r)
    	{
    		fir[x] = tree[x] = pnt[l];
    		return;
    	}
    	int mid = (l + r) >> 1;
    	build(x << 1, l, mid);
    	build(x << 1 | 1, mid + 1, r);
    	fir[x] = tree[x] = pushup(tree[x << 1], tree[x << 1 | 1]);
    }
    
    void erase(int x, int l, int r, int ll, int rr)
    {
    	//tree[x] *= val[x];
    	//tree[x] = fir[x] * val[x];
    	if (ll <= l && r <= rr)
    	{
    		tree[x] = 0; val[x] = 2;
    		return;
    	}
    	spread(x);
    	int mid = (l + r) >> 1;
    	if (ll <= mid)
    	{
    		erase(x << 1, l, mid, ll, rr);
    	}
    	if (rr > mid)
    	{
    		erase(x << 1 | 1, mid + 1, r, ll, rr);
    	}
    	tree[x] = pushup(tree[x << 1], tree[x << 1 | 1]);
    }
    
    int calc(int k)
    {
    	//insert(1, 1, 1, 1, n);
    	val[1] = 1;
    	tree[1] = fir[1];
    	int ret = 0;
    	while (tree[1])
    	{
    		int del = Kfat(tree[1], k);
    		erase(1, 1, n, dfn[del], dfn[del] + sze[del] - 1);
    		ret++;
    	}
    	return ret;
    }
    
    
    as 0.4123
  • 相关阅读:
    LVS的优点和缺点
    linux系统中如何查看日志 (常用命令)
    linux隐藏病毒处理(top查询us占用70%左右,却没有CPU高使用的进程)
    linux系统批量注释的方法
    linux系统硬件时间命令
    LINUX服务器设置只允许密钥登陆
    linux系统脚本放在后台与前台
    Kworkerd恶意挖矿分析
    HAProxy的优点
    find命令
  • 原文地址:https://www.cnblogs.com/Linshey/p/13873198.html
Copyright © 2011-2022 走看看