zoukankan      html  css  js  c++  java
  • 【38.05%】【BZOJ 4154】Generating Synergy

    Time Limit: 10 Sec  Memory Limit: 512 MB
    Submit: 389  Solved: 148
    [Submit][Status][Discuss]

    Description

    给定一棵以1为根的有根树,初始所有节点颜色为1,每次将距离节点a不超过l的a的子节点染成c,或询问点a的颜色

    Input

    第一行一个数T,表示数据组数
    接下来每组数据的第一行三个数n,c,q表示结点个数,颜色数和操作数
    接下来一行n-1个数描述2..n的父节点
    接下来q行每行三个数a,l,c
    若c为0,表示询问a的颜色
    否则将距离a不超过l的a的子节点染成c

    Output

    设当前是第i个操作,y_i为本次询问的答案(若本次操作是一个修改则y_i为0),令z_i=i*y_i,请输出z_1+z_2+...+z_q模10^9+7

    Sample Input

    1
    4 3 7
    1 2 2
    3 0 0
    2 1 3
    3 0 0
    1 0 2
    2 0 0
    4 1 1
    4 0 0

    Sample Output

    32

    HINT



    第1,3,5,7的询问的答案分别为1,3,3,1,所以答案为 1*1+2*0+3*3+4*0+5*3+6*0+7*1=32.

    数据范围:

    对于100%的数据T<=6,n,m,c<=10^5,

    1<=a<=n,0<=l<=n,0<=c<=c

    【题解】

    考虑对象是某个节点的子节点->dfs序。

    但是dfs序里面的节点不一定满足距离不大于l

    怎么办?

    再加一个限制.即树的深度。

    既要满足某个dfs序。又要满足树的深度在某个范围内。这样就能确定是哪些点了。

    然后把某个节点的dfs序和深度当做是坐标(x,y)
    则满足的坐标范围就是(b[x]..e[x],dep[x]..dep[x]+l);

    染色的时候打上懒惰标记a_t表示以下节点全部覆盖。然后这个覆盖是在a_t时间发生的,覆盖的颜色是a_c

    d_t表示单个节点被覆盖,然后这个覆盖是在d_t时发生的,覆盖的颜色是d_c;

    要确定某个点被染成了什么颜色的时候。就从那个节点一直往爸爸节点方向找。

    找到a_t时间最大的。它对应的a_c颜色就是这个节点被覆盖的颜色(之所以要这样做是因为我们并没有像线段树一样把懒惰标记往下传递)。只好手动找了。

    我这样说可能有点抽象。你们先看代码吧。

    【代码】

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    const int MAXN = 109000;
    const int mod = 1000000007;
    
    struct point
    {
    	int d[2], ma_x[2], mi_n[2], l, r, fa, bianhao, d_c, d_t, a_c, a_t;
    };
    
    int n, c, q, b[MAXN], e[MAXN], num, dep[MAXN] = { 0 }, dot[MAXN], root, now;
    vector <int> a[MAXN];
    point t[MAXN];
    long long ans;
    
    
    void input_data()
    {
    	scanf("%d%d%d", &n, &c, &q);
    	for (int i = 2; i <= n; i++)
    	{
    		int father_i;
    		scanf("%d", &father_i);
    		a[father_i].push_back(i);
    	}
    }
    
    void init()
    {
    	for (int i = 1; i <= MAXN - 1; i++)
    		a[i].clear();
    	num = 0;
    	memset(dep, 0, sizeof(dep));
    }
    
    void dfs(int x)//深搜获取dfs序和每个节点的深度
    {
    	b[x] = ++num;
    	int len = a[x].size();
    	for (int i = 0; i <= len - 1; i++)
    	{
    		int y = a[x][i];
    		dep[y] = dep[x] + 1;
    		dfs(y);
    	}
    	e[x] = num;
    }
    
    bool cmp(point a, point b)
    {
    	return a.d[now] < b.d[now];
    }
    
    void up_data(int rt)
    {
    	int l = t[rt].l, r = t[rt].r;
    	for (int i = 0; i <= 1; i++)
    	{
    		if (l)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[l].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[l].mi_n[i]);
    		}
    		if (r)
    		{
    			t[rt].ma_x[i] = max(t[rt].ma_x[i], t[r].ma_x[i]);
    			t[rt].mi_n[i] = min(t[rt].mi_n[i], t[r].mi_n[i]);
    		}
    	}
    }
    
    int build(int begin, int end, int fa, int fx)
    {
    	int m = (begin + end) >> 1;
    	now = fx;
    	nth_element(t + begin, t + m, t + end + 1, cmp);
    	dot[t[m].bianhao] = m; t[m].fa = fa;
    	for (int i = 0; i <= 1; i++)
    		t[m].ma_x[i] = t[m].mi_n[i] = t[m].d[i];
    	if (begin < m)
    		t[m].l = build(begin, m - 1, m, 1 - fx);
    	else
    		t[m].l = 0; //有多组数据。所以要置0.不然会RE或WA
    	if (m < end)
    		t[m].r = build(m + 1, end, m, 1 - fx);
    	else
    		t[m].r = 0;
    	up_data(m);
    	return m;
    }
    
    void updata(int rt, int time, int color, int xl, int xr, int yl, int yr)
    {
    	if (!rt)
    		return;
    	if (xl <= t[rt].mi_n[0] && t[rt].ma_x[0] <= xr &&
    		yl <= t[rt].mi_n[1] && t[rt].ma_x[1] <= yr) //这个节点以下的节点完全在所求范围内。
    	{
    		t[rt].a_c = t[rt].d_c = color;
    		t[rt].a_t = t[rt].d_t = time;
    		return;
    	}
    	if (xr < t[rt].mi_n[0] || xl > t[rt].ma_x[0] ||
    		yr < t[rt].mi_n[1] || yl > t[rt].ma_x[1]) //这是继续往下不可能有解的情况
    		return;
    	if (xl <= t[rt].d[0] && t[rt].d[0] <= xr && yl <= t[rt].d[1] && t[rt].d[1] <= yr)
    	{
    		t[rt].d_c = color;
    		t[rt].d_t = time;
    	}
    	updata(t[rt].l, time, color, xl, xr, yl, yr);
    	updata(t[rt].r, time, color, xl, xr, yl, yr);
    }
    
    int query(int rt)
    {
    	int ti = t[rt].d_t, ci = t[rt].d_c;//先获取单个节点的染色
    	rt = t[rt].fa;
    	while (rt)//然后往上看有没有整个子树的染色。染色时间靠后的就是我们所需的。
    	{
    		if (t[rt].a_t > ti)
    		{
    			ti = t[rt].a_t;
    			ci = t[rt].a_c;
    		}
    		rt = t[rt].fa;
    	}
    	return ci;
    }
    
    void get_ans()
    {
    	dfs(1);
    	for (int i = 1; i <= n; i++)
    	{
    		t[i].d[0] = b[i];
    		t[i].d[1] = dep[i];
    		t[i].bianhao = i;
    		t[i].a_c = t[i].d_c = 1;//一开始全部覆盖的是1.
    		t[i].a_t = t[i].d_t = 0;
    		t[i].fa = 0;
    	}
    	root = build(1, n, 0, 0);
    	ans = 0;
    	for (int i = 1; i <= q; i++)
    	{
    		int x, y, z;
    		scanf("%d%d%d", &x, &y, &z);
    		if (z)
    		{
    			if (y == 0 || b[x] == e[x])//距离为0 或没有子节点。
    			{
    				t[dot[x]].d_c = z;
    				t[dot[x]].d_t = i;
    			}
    			else
    				updata(root, i, z, b[x], e[x], dep[x], dep[x] + y);
    		}
    		else
    		{
    			int temp = query(dot[x]);
    			long long temp1 = temp;
    			long long temp2 = i;
    			ans = (ans + temp2*temp1) % mod;
    		}
    	}
    	printf("%lld
    ", ans);
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    	int T;
    	scanf("%d", &T);
    	while (T--)
    	{
    		init();
    		input_data();
    		get_ans();
    	}
    	return 0;
    }


  • 相关阅读:
    最大流之dinic
    HDU 2485
    最小费用最大流
    HDU 1533
    HDU 1402
    HDU 1498
    HDU 1281
    Codeforces 283E Cow Tennis Tournament 线段树 (看题解)
    Codeforces 983E NN country 思维 (看题解)
    Codeforces 494D Birthday 树形dp (看题解)
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632238.html
Copyright © 2011-2022 走看看