zoukankan      html  css  js  c++  java
  • BZOJ 3626 LCA

    Description

    给出一个(n)个节点的有根树(编号为(0)到n-1,根节点为(0))。一个点的深度定义为这个节点到根的距离(+1)
    (dep_{i})表示点(i)的深度,(LCA(i,j))表示(i)(j)的最近公共祖先。
    (q)次询问,每次询问给出(l,r,z),求(sum_{i=l}^{r}dep_{LCA(i,z)})。(即,求在[l,r]区间内的每个节点i与z的最近公共祖先的深度之和)

    Input

    第一行2个整数(n,q)
    接下来(n-1)行,分别表示点(1)到点(n-1)的父节点编号。
    接下来(q)行,每行(3)个整数(l,r,z)

    Output

    输出(q)行,每行表示一个询问的答案。每个答案对(201314)取模输出。

    Sample Input

    5 2
    0
    0
    1
    1
    1 4 3
    1 4 2

    Sample Output

    8
    5

    HINT

    (5)组数据,(n)(q)的规模分别为(10000,20000,30000,40000,50000)

    这题被czh随便秒。做法其实很简单,对于(dep_{LCA(a,b)}),我们把(a)到根的路径上的点的点权(+1),询问时我们就可以直接查询(b)到根路径上的点权和即可。所以这题就可以做了。
    问题在于询问,好像可以建主席树(可能是我口胡的),但离线算法可能更好写。
    首先我们知道询问满足可并性$$ans_{l,r,z} = ans_{0,r,z}-ans_{0,l-1,z}$$
    所以我们可以将一个询问拆成两个,排序后按顺序修改,分开计算答案即可。(用(l,r)修改,(z)来查询)。
    树链剖分和lct都可以写。

    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    
    typedef long long ll;
    #define rhl (201314)
    #define maxn (100010)
    int N,Q,side[maxn],toit[maxn],next[maxn],tot,id[maxn],tree[maxn*4],inc[maxn*4];
    int size[maxn],heavy[maxn],cnt,father[maxn],top[maxn],ans[maxn],key[maxn*4];
    struct SCAN
    {
    	int pos,key,ord,sign;
    	friend inline bool operator <(const SCAN &a,const SCAN &b) { return a.pos < b.pos; }
    }scan[maxn*2];
    
    inline void add(int a,int b) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; }
    
    inline void dfs(int now)
    {
    	size[now] = 1; heavy[now] = N;
    	for (int i = side[now];i;i = next[i])
    	{
    		dfs(toit[i]);
    		father[toit[i]] = now,size[now] += size[toit[i]];
    		if (size[toit[i]] > size[heavy[now]]) heavy[now] = toit[i];
    	}
    }
    
    inline void Div(int now,int Top)
    {
    	id[now] = ++tot; top[now] = Top;
    	if (side[now]) Div(heavy[now],Top);
    	for (int i = side[now];i;i = next[i])
    		if (toit[i] != heavy[now]) Div(toit[i],toit[i]);
    }
    
    inline void pushdown(int now,int l,int r)
    {
    	if (!inc[now]) return;
    	if (l < r)
    	{
    		int mid = (l + r) >> 1;
    		tree[now<<1] += (ll)inc[now]*(mid-l+1)%rhl;
    		tree[now<<1|1] += (ll)inc[now]*(r-mid)%rhl;
    		if (tree[now<<1] >= rhl) tree[now<<1] -= rhl;
    		if (tree[now<<1|1] >= rhl) tree[now<<1|1] -= rhl;
    		inc[now << 1] += inc[now]; inc[now << 1|1] += inc[now];
    		if (inc[now<<1] >= rhl) inc[now<<1] -= rhl;
    		if (inc[now<<1|1] >= rhl) inc[now<<1|1] -= rhl;
    	}
    	inc[now] = 0;
    }
    
    inline void modify(int ql,int qr,int l,int r,int now)
    {
    	pushdown(now,l,r);
    	if (ql <= l&&r <= qr)
    	{
    		tree[now] += r-l+1; inc[now]++;
    		if (inc[now] >= rhl) inc[now] -= rhl;
    		if (tree[now] >= rhl) tree[now] -= rhl; return;
    	}
    	int mid = (l + r) >> 1;
    	if (qr <= mid) modify(ql,qr,l,mid,now<<1);
    	else if (ql > mid) modify(ql,qr,mid+1,r,now<<1|1);
    	else modify(ql,mid,l,mid,now<<1),modify(mid+1,qr,mid+1,r,now<<1|1);
    	tree[now] = tree[now<<1]+tree[now<<1|1];
    	if (tree[now] >= rhl) tree[now] -= rhl;
    }
    
    inline int ask(int ql,int qr,int l,int r,int now)
    {
    	pushdown(now,l,r);
    	if (ql <= l&&r <= qr) return tree[now];
    	int mid = (l + r) >> 1,ret;
    	if (qr <= mid) ret = ask(ql,qr,l,mid,now<<1);
    	else if (ql > mid) ret = ask(ql,qr,mid+1,r,now<<1|1);
    	else ret = ask(ql,mid,l,mid,now<<1)+ask(mid+1,qr,mid+1,r,now<<1|1);
    	if (ret >= rhl) ret -= rhl;
    	return ret;
    }
    
    inline void insert(int now)
    {
    	for (;top[now];now = father[top[now]]) modify(id[top[now]],id[now],1,N,1);
    	modify(id[0],id[now],1,N,1);
    }
    
    inline int query(int now)
    {
    	int ret = 0;
    	for (;top[now];now = father[top[now]])
    	{
    		ret += ask(id[top[now]],id[now],1,N,1);
    		if (ret >= rhl) ret -= rhl;
    	}
    	ret += ask(id[0],id[now],1,N,1);
    	if (ret >= rhl) ret -= rhl;
    	return ret;
    }
    
    int main()
    {
    	freopen("3626.in","r",stdin);
    	freopen("3626.out","w",stdout);
    	scanf("%d %d",&N,&Q);
    	for (int i = 1,a;i < N;++i)	scanf("%d",&a),add(a,i);
    	dfs(0); Div(0,0);
    	for (int i = 1,l,r,z;i <= Q;++i)
    	{
    		scanf("%d %d %d",&l,&r,&z);
    		scan[(i << 1)-1] = (SCAN){l-1,z,i,-1};
    		scan[i << 1] = (SCAN){r,z,i,1};
    	}
    	sort(scan+1,scan+(Q<<1|1));
    	for (int i = 1,j = -1;i <= (Q<<1);++i)
    	{
    		while (j < N&&j + 1 <= scan[i].pos) insert(++j);
    		ans[scan[i].ord] += scan[i].sign*query(scan[i].key);
    		if (ans[scan[i].ord] >= rhl) ans[scan[i].ord] -= rhl;
    		if (ans[scan[i].ord] < 0) ans[scan[i].ord] += rhl;
    	}
    	for (int i = 1;i <= Q;++i) printf("%d
    ",ans[i]);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    C++实例 分解质因数
    C++语言 堆排序
    下载吧压缩工具(360超级注释压缩)
    unresolved external symbol __endthreadex错误解决
    C语言 常用函数A
    C++实例 获取进程所在文件夹
    IIS 服务或万维网发布服务,或者依赖这 服务可能在启动期间发生错误或者已禁用
    字體的css
    ubuntu下mono初体验<一>
    NOSQL之mongodb简介及安装 for linux(centOS)<二>
  • 原文地址:https://www.cnblogs.com/mmlz/p/4517412.html
Copyright © 2011-2022 走看看