zoukankan      html  css  js  c++  java
  • P5327 [ZJOI2019]语言

    题目

    P5327 [ZJOI2019]语言

    分析

    线段树合并+树上差分。

    首先我们发现答案其实就是:对于每一个点来说的连通块大小之和。

    那么现在问题在于怎么来维护这个连通块的大小。

    我们可以考虑对每一个点开一个线段树,保存:(dfn) 序列对应的点被路径覆盖次数和长度。

    然后对于这样一类树上路径修改且每个点都要查询的问题,我们可以考虑使用线段树合并+树上差分来解决。

    那么这道题就显而易见了,是直接打上两个 (+1) 标记,然后在 (fa[lca]) 处打上 (-2) 的标记。

    接下来就是直接线段树合并,重点在于怎么具体维护有多少个点,我们发现如果当前区间的都是大于 (0) 的话,那么个数就是 (r-l+1) ,因为我们每一次修改的时候,一定是一个连续的链(从下到上)。

    这是由我们树剖来决定的,也就是有 (logn) 个区间要进行修改的意思。

    具体见代码。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    template <typename T>
    inline void read(T &x){
    	x=0;char ch=getchar();bool f=false;
    	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
    	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    	x=f?-x:x;
    	return ;
    }
    template <typename T>
    inline void write(T x){
    	if(x<0) putchar('-'),x=-x;
    	if(x>9) write(x/10);
    	putchar(x%10^48);
    	return ;
    }
    const int N=1e5+5;
    #define ll long long
    int n,m;
    ll Ans;
    int head[N],nex[N<<1],to[N<<1],idx;
    inline void add(int u,int v){
    	nex[++idx]=head[u];
    	to[idx]=v;
    	head[u]=idx;
    	return ;
    }
    int fa[N],dep[N],siz[N],son[N],top[N],dfn[N],rev[N],DFN;
    void dfs1(int x,int f){
    	fa[x]=f,dep[x]=dep[f]+1,siz[x]=1;
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==f) continue;
    		dfs1(y,x);siz[x]+=siz[y];
    		if(siz[y]>siz[son[x]]) son[x]=y;
    	}
    	return ;
    }
    void dfs2(int x){
    	if(x==son[fa[x]]) top[x]=top[fa[x]];
    	else top[x]=x;
    	dfn[x]=++DFN,rev[DFN]=x;
    	if(son[x]) dfs2(son[x]);
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==fa[x]||y==son[x]) continue;
    		dfs2(y);
    	}
    	return ;
    }
    inline int QueryLca(int x,int y){
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	return dep[x]<dep[y]?x:y;
    }
    int Root[N];
    struct SGT{
    	int sum,num,ls,rs;
    	#define sum(x) t[x].sum
    	#define num(x) t[x].num
    	#define ls(x) t[x].ls
    	#define rs(x) t[x].rs
    }t[N*250];
    int cur;
    void Modify(int &x,int l,int r,int ql,int qr,int v){
    	if(!x) x=++cur;
    	if(ql<=l&&qr>=r) return sum(x)+=v,num(x)=(sum(x)>0?(r-l+1):(num(ls(x))+num(rs(x)))),void();
    	int mid=l+r>>1;
    	if(ql<=mid) Modify(ls(x),l,mid,ql,qr,v);
    	if(qr>mid) Modify(rs(x),mid+1,r,ql,qr,v);
    	num(x)=(sum(x)>0?(r-l+1):(num(ls(x))+num(rs(x))));
    	return ;
    }
    int Query(int x,int l,int r,int ql,int qr){
    	if(!x) return 0;
    	if(ql<=l&&r<=qr) return num(x);
    	int mid=l+r>>1,res=0;
    	if(ql<=mid) res+=Query(ls(x),l,mid,ql,qr);
    	if(qr>mid) res+=Query(rs(x),mid+1,r,ql,qr);
    	return res;
    }
    int Merge(int x,int y,int l,int r){
    	if(!x||!y) return x|y;
    	sum(x)+=sum(y);int mid=l+r>>1;
    	ls(x)=Merge(ls(x),ls(y),l,mid),rs(x)=Merge(rs(x),rs(y),mid+1,r);
    	num(x)=(sum(x)>0?(r-l+1):(num(ls(x))+num(rs(x))));
    	return x;
    }
    typedef pair<int,int> PII;
    PII path[N];
    int Cnt;
    void GetSeq(int x,int y){
    	Cnt=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]]) swap(x,y);
    		path[++Cnt]=make_pair(dfn[top[x]],dfn[x]);
    		x=fa[top[x]];
    	}
    	if(dep[x]<dep[y]) swap(x,y);
    	path[++Cnt]=make_pair(dfn[y],dfn[x]);
    	return ;
    }
    void Solve(int x){
    	Modify(Root[x],1,n,dfn[x],dfn[x],1);
    	for(int i=head[x];i;i=nex[i]){
    		int y=to[i];
    		if(y==fa[x]) continue;
    		Solve(y);Root[x]=Merge(Root[x],Root[y],1,n);
    	}
    	Ans+=Query(Root[x],1,n,1,n)-1;
    	Modify(Root[x],1,n,dfn[x],dfn[x],-1);
    	return ;
    }
    int main(){
    	read(n),read(m);
    	for(int i=1;i<n;i++){
    		int u,v;read(u),read(v);
    		add(u,v),add(v,u);
    	}
    	dfs1(1,0);dfs2(1);
    	for(int i=1;i<=m;i++){
    		int s,t;read(s),read(t);
    		int lca=QueryLca(s,t),f=fa[lca];
    		GetSeq(s,t);
    		for(int j=1;j<=Cnt;j++){
    			Modify(Root[s],1,n,path[j].first,path[j].second,1);
    			Modify(Root[t],1,n,path[j].first,path[j].second,1);
    			Modify(Root[f],1,n,path[j].first,path[j].second,-2);
    		}
    	}
    	Solve(1);
    	write(Ans/2);
    	return 0;
    }
    
  • 相关阅读:
    案例(用封装的ajax加载数据库的数据到页面)
    案例(用封装的ajax函数检查用户名)
    Verilog中的UDP
    FPGA中的“门”
    反馈的基础概述
    集成运放四种组态
    阻抗匹配处理方式
    关于阻抗匹配的理解
    关于输入阻抗和输出阻抗的理解
    电压跟随器的一点理解
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14778531.html
Copyright © 2011-2022 走看看