Codeforces Round #200 (Div. 1) D. Water Tree (线段树+DFS序)

首先很容易想到这是一道DFS序的题目,每个人根据DFS序建立自己管辖的区间。 如图1


图1

黑色是点,红色是他们管辖的区间。
那么灌水操作就是一个区间更新,比如对1倒水,那么就是把区间1-5中的每个点都更新成1。

然后就是倒水操作,因为这个倒水操作比较特殊,如果把一个点倒掉等于把它的祖先节点的都倒掉。如果直接用并查集维护的话一个个倒过去肯定是TLE的。我们发现因为一个点的儿子中存在一个点被倒掉的话,那么它也一定是空的。可以用反证法,如果它是满的,说明肯定从它往下倒过,那么它的儿子节点就不可能存在空。

所以对操作二在线段树上进行单点更新。然后查询的时候进行区间查询,查询它管辖范围内的区间最小值。

然后还有一个坑点就是在进行灌水操作的时候,要判断它的父亲中的水量是否为空,如果为空的话,在更新完这个点之后还得把父亲重新倒掉水。因为我们在倒水的时候再线段树上只是进行单点更新。如图,如果3被倒去了水



那么在单点更新后是这样的,从图1我们可以看出这时候1和2应该都是空的。如果对3进行灌水操作,线段树会变成这样。



显然这时查询区间最小值已经全部变成了1,无法得到1和2水池状态是0的结果了。
所以要在最后对2重新进行倒水操作。

这样就可以了。

#include 
#include 
#include 
#include 
#include 
#define lson i<<1,l,mid
#define rson i<<1|1,mid+1,r
#define getmid int mid=(l+r)>>1
using namespace std;
const int MAXN = 500010;
int tree[MAXN*4],vis[MAXN];
int lazy[MAXN*4],fa[MAXN];
int n,m,tot,L[MAXN],R[MAXN];

vector > g(MAXN);

void dfs(int u,int father)
{
    fa[u]=father;
    vis[u]=true;
    L[u]=++tot;
    int sz=g[u].size();
    for (int i=0;imid) update(rson,x,y,op);
    pushup(i);
}

void update(int i,int l,int r,int x,int op)
{
    if (l==r)
    {
        tree[i]=op;
        return;
    }
    pushdown(i);
    getmid;
    if (x<=mid) update(lson,x,op);
    else update(rson,x,op);
    pushup(i);
}

int query(int i,int l,int r,int x,int y)
{
    if (x<=l && r<=y)
    {
        return tree[i];
    }
    getmid;
    pushdown(i);
    int ans=100;
    if (x<=mid) ans=min(ans,query(lson,x,y));
    if (y>mid) ans=min(ans,query(rson,x,y));
    return ans;
}

int main()
{
    int n,q;
    scanf("%d",&n);
    for (int i=1;i

你可能感兴趣的:(Codeforces Round #200 (Div. 1) D. Water Tree (线段树+DFS序))