zoukankan      html  css  js  c++  java
  • 洛谷 P4211 [LNOI2014]LCA(树剖+线段树,差分)

    传送门


    解题思路

    调了一晚上。。紫题果然不是我现在能做的。。
    首先考虑如何把多个deep的和转化成可以快速求出来的东西:
    我们可以对于每个[l,r],把每个点到根节点的路径上的点权++(初始为0),这样对于每个询问(l,r,z),答案即为z到根节点的路径上的点权和。-----操作1
    但是对于每个询问都操作一遍很显然会tle,所以再利用差分思想,把(l,r,z)转化为(1,r,z)-(1,l-1,z),具体来说,就是每次操作完后不清空点权,对询问进行排序,然后根据点编号从小到大依次进行操作1,并保存答案。
    这样最多只会进行n次操作1。
    具体如何实现操作1?
    对于像这样路径上的区间加、区间求和操作,可以方便的使用树剖+线段树维护。

    AC代码

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<iomanip>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int maxn=(5e4+5)*4;
    const int mod=201314;
    struct node{
    	int v,next;
    }e[maxn];
    struct Node{
    	int r,z,v,id; 
    }q[maxn];
    int n,m,lazy[maxn],p[maxn],ans[maxn],cnt,num,deep[maxn],siz[maxn],son[maxn];
    int tp[maxn],dfn[maxn],f[maxn],rk[maxn],sum[maxn];
    bool cmp(Node a,Node b){
    	return a.r<b.r;
    }
    void insert(int u,int v){
    	cnt++;
    	e[cnt].v=v;
    	e[cnt].next=p[u];
    	p[u]=cnt;
    }
    void dfs1(int u,int dep){
    	deep[u]=dep;
    	siz[u]=1;
    	for(int i=p[u];i!=-1;i=e[i].next){
    		int v=e[i].v;
    		dfs1(v,dep+1);
    		siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]]) son[u]=v;
    	}
    }
    void dfs2(int u,int top){
    	tp[u]=top;
    	dfn[u]=++num;
    	rk[num]=u;
    	if(!son[u]) return;
    	dfs2(son[u],top);
    	for(int i=p[u];i!=-1;i=e[i].next){
    		if(e[i].v==son[u]) continue;
    		dfs2(e[i].v,e[i].v);
    	}
    }
    inline void pushdown(int id,int l,int r){
    	if(lazy[id]){
    		int mid=(l+r)/2;
    		sum[id*2]+=lazy[id]*(mid-l+1);
    		sum[id*2+1]+=lazy[id]*(r-mid);
    		lazy[id*2]+=lazy[id];
    		lazy[id*2+1]+=lazy[id];
    		lazy[id]=0;
    	}
    }
    inline void pushup(int id){
    	sum[id]=sum[id*2]+sum[id*2+1];
    }
    void update(int id,int l,int r,int x,int y){
    	if(x<=l&&r<=y){
    		lazy[id]++;
    		sum[id]+=(r-l+1);
    		return;
    	}
    	int mid=(l+r)/2;
    	pushdown(id,l,r);
    	if(x<=mid) update(id*2,l,mid,x,y);
    	if(y>mid) update(id*2+1,mid+1,r,x,y);
    	pushup(id);
    }
    int query(int id,int l,int r,int x,int y){
    	if(x<=l&&r<=y){
    		return sum[id]%mod;
    	}
    	int mid=(l+r)/2,res=0;
    	pushdown(id,l,r);
    	if(x<=mid) res+=query(id*2,l,mid,x,y);
    	if(y>mid) res+=query(id*2+1,mid+1,r,x,y);
    	return res%mod;
    }
    void add(int x){
    	while(tp[x]!=1){
    		update(1,1,n,dfn[tp[x]],dfn[x]);
    		x=f[tp[x]];
    	}
    	update(1,1,n,1,dfn[x]);
    }
    int getans(int x){
    	int res=0;
    	while(tp[x]!=1){
    		res=(res+query(1,1,n,dfn[tp[x]],dfn[x]))%mod;
    		x=f[tp[x]];
    	}
    	res+=query(1,1,n,1,dfn[x]);
    	return res%mod;
    }
    int main(){
    	ios::sync_with_stdio(false);
    	memset(p,-1,sizeof(p));
    	cin>>n>>m;
    	f[1]=1;
    	for(int i=2;i<=n;i++){
    		cin>>f[i];
    		f[i]++;
    		insert(f[i],i);
    	}
    	dfs1(1,1);
    	dfs2(1,1);
    	for(int i=1;i<=m;i++){
    		int l,r,z;
    		cin>>l>>r>>z;
    		l++;
    		r++;
    		z++;
    		q[i].r=l-1;
    		q[i].z=z;
    		q[i].v=-1;
    		q[i].id=i;
    		q[i+m].r=r;
    		q[i+m].z=z;
    		q[i+m].v=1;
    		q[i+m].id=i;
    	}
    	sort(q+1,q+2*m+1,cmp);
    	int now=1;
    	for(int i=1;i<=2*m;i++){
    		while(q[i].r==0) i++;
    		while(now<=n&&now<=q[i].r){
    			add(now);
    			now++;
    		}
    		ans[q[i].id]+=q[i].v*getans(q[i].z);
    	}
    	for(int i=1;i<=m;i++) cout<<(ans[i]%mod+mod)%mod<<endl;
        return 0;
    }
    
  • 相关阅读:
    线程私有数据
    C
    Zend_Json 简介 --(手冊)
    Spring之AOP实现面向切面编程
    JDBC框架
    NYOJ15-括号匹配(二)-区间DP
    SDUTOJ 贪心 -商人小鑫
    Java 8 类型转换及改进
    java内存结构(执行时数据区域)
    Android Studio 编译Gradle提示编码错误
  • 原文地址:https://www.cnblogs.com/yinyuqin/p/15303048.html
Copyright © 2011-2022 走看看