zoukankan      html  css  js  c++  java
  • 【洛谷P5327】语言

    题目

    题目链接:https://www.luogu.com.cn/problem/P5327
    九条可怜是一个喜欢规律的女孩子。按照规律,第二题应该是一道和数据结构有关的题。
    在一个遥远的国度,有 \(n\) 个城市。城市之间有 \(n − 1\) 条双向道路,这些道路保证了任何两个城市之间都能直接或者间接地到达。
    在上古时代,这 \(n\) 个城市之间处于战争状态。在高度闭塞的环境中,每个城市都发展出了自己的语言。而在王国统一之后,语言不通给王国的发展带来了极大的阻碍。为了改善这种情况,国王下令设计了 \(m\) 种通用语,并进行了 \(m\) 次语言统一工作。在第 \(i\) 次统一工作中,一名大臣从城市 \(s_i\) 出发,沿着最短的路径走到了 \(t_i\),教会了沿途所有城市(包括 \(s_i, t_i\))使用第 \(i\) 个通用语。
    一旦有了共通的语言,那么城市之间就可以开展贸易活动了。两个城市 \(u_i, v_i\) 之间可以开展贸易活动当且仅当存在一种通用语 \(L\) 满足 \(u_i\)\(v_i\) 最短路上的所有城市(包括 \(u_i, v_i\)),都会使用 \(L\)
    为了衡量语言统一工作的效果,国王想让你计算有多少对城市 \((u, v)\ (u < v)\),他们之间可以开展贸易活动。
    \(n,m\leq 10^5\)

    思路

    好优美的数据结构题。
    直接结算的话肯定会算重。需要找一些性质。
    考虑对于任意一个点 \(x\),求出有多少个点能与他产生贡献。很显然,这些点一定会形成一个子连通块。而我们需要计算的就是这个子连通块的大小。
    如果一条路径 \((s_i,t_i)\) 包含了点 \(x\),那么 \(s_i,t_i\) 之间的所有点都在这个连通块内。所以这个连通块的大小就是所有包含 \(x\) 的路径两端所有点的最小生成树的大小。
    如果已经知道了若干个点,需要求出他们最小生成树的大小,可以按照 dfs 序排序后,每一个点的深度之和减去相邻的点 lca 的深度之和。
    按照 dfs 序为下标建立一棵线段树,对于点 \(x\),把所有包含 \(x\) 的路径的 \(s\)\(t\) 在线段树上打上标记,然后线段树区间合并的时候可以维护这个区间的答案。
    最后点 \(x\) 的贡献就是线段树根节点的答案,再减去 dfs 序最小最大两个点的 lca 的深度。
    显然不可能对于每个点都暴力遍历一次所有包含这个点的路径。把一条路径 \((s,t)\) 拆成两条链,在 \(s,t\) 打上 \(+1\) 的标记,\(\text{lca}(s,t),\text{fa}[\text{lca}(s,t)]\) 处打上 \(-1\) 的标记,然后动态开点线段树合并即可。
    由于每次线段树区间合并都要求一次 lca,时间复杂度是 \(O(n\log^2 n)\)。采用 \(O(n\log n)-O(1)\) lca 可以做到 \(O(n\log n)\)

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=100010,LG=18;
    int n,m,tot,id[N],rk[N],dep[N],head[N],rt[N],f[N][LG+1];
    ll ans;
    vector<int> a[N];
    
    struct edge
    {
    	int next,to;
    }e[N*2];
    
    void add(int from,int to)
    {
    	e[++tot]=(edge){head[from],to};
    	head[from]=tot;
    }
    
    void dfs1(int x,int fat)
    {
    	f[x][0]=fat; dep[x]=dep[fat]+1;
    	id[x]=++tot; rk[tot]=x;
    	for (int i=1;i<=LG;i++)
    		f[x][i]=f[f[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=fat) dfs1(v,x);
    	}
    }
    
    int lca(int x,int y)
    {
    	if (!x || !y) return 0;
    	if (dep[x]<dep[y]) swap(x,y);
    	for (int i=LG;i>=0;i--)
    		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if (x==y) return x;
    	for (int i=LG;i>=0;i--)
    		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    
    struct SegTree
    {
    	int tot,lc[N<<5],rc[N<<5],cnt[N<<5],lp[N<<5],rp[N<<5];
    	ll ans[N<<5];
    	
    	void pushup(int x)
    	{
    		lp[x]=lp[lc[x]] ? lp[lc[x]] : lp[rc[x]];
    		rp[x]=rp[rc[x]] ? rp[rc[x]] : rp[lc[x]];
    		ans[x]=ans[lc[x]]+ans[rc[x]]-dep[lca(rp[lc[x]],lp[rc[x]])];
    	}
    	
    	void updleaf(int x,int k)
    	{
    		if (cnt[x]) lp[x]=rp[x]=rk[k],ans[x]=dep[rk[k]];
    		if (!cnt[x]) lp[x]=rp[x]=ans[x]=0;
    	}
    	
    	int merge(int x,int y,int l,int r)
    	{
    		if (!x || !y) return x|y;
    		if (l==r) { cnt[x]+=cnt[y]; updleaf(x,l); return x; }
    		int mid=(l+r)>>1;
    		lc[x]=merge(lc[x],lc[y],l,mid);
    		rc[x]=merge(rc[x],rc[y],mid+1,r);
    		pushup(x);
    		return x;
    	}
    	
    	int update(int x,int l,int r,int k,int v)
    	{
    		if (!x) x=++tot;
    		if (l==r) { cnt[x]+=v; updleaf(x,l); return x; }
    		int mid=(l+r)>>1;
    		if (k<=mid) lc[x]=update(lc[x],l,mid,k,v);
    		if (k>mid) rc[x]=update(rc[x],mid+1,r,k,v);
    		pushup(x);
    		return x;
    	}
    }seg;
    
    void dfs2(int x)
    {
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (v!=f[x][0])
    		{
    			dfs2(v);
    			rt[x]=seg.merge(rt[x],rt[v],1,n);
    		}
    	}
    	for (int i=0;i<(int)a[x].size();i++)
    	{
    		int y=abs(a[x][i]),z=(a[x][i]>0)?1:-1;
    		rt[x]=seg.update(rt[x],1,n,id[y],z);
    	}
    	ans+=seg.ans[rt[x]]-dep[lca(seg.lp[rt[x]],seg.rp[rt[x]])];
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1,x,y;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y); add(y,x);
    	}
    	tot=0; dfs1(1,0);
    	for (int i=1,x,y;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		int p=lca(x,y);
    		a[x].push_back(x); a[x].push_back(y);
    		a[y].push_back(x); a[y].push_back(y);
    		a[p].push_back(-x); a[p].push_back(-y);
    		a[f[p][0]].push_back(-x); a[f[p][0]].push_back(-y);
    	}
    	dfs2(1);
    	printf("%lld",ans/2LL);
    	return 0;
    }
    
  • 相关阅读:
    从零开始——PowerShell应用入门(全例子入门讲解)
    详解C# Tuple VS ValueTuple(元组类 VS 值元组)
    How To Configure VMware fencing using fence_vmware_soap in RHEL High Availability Add On——RHEL Pacemaker中配置STONITH
    DB太大?一键帮你收缩所有DB文件大小(Shrink Files for All Databases in SQL Server)
    SQL Server on Red Hat Enterprise Linux——RHEL上的SQL Server(全截图)
    SQL Server on Ubuntu——Ubuntu上的SQL Server(全截图)
    微软SQL Server认证最新信息(17年5月22日更新),感兴趣的进来看看哟
    Configure Always On Availability Group for SQL Server on RHEL——Red Hat Enterprise Linux上配置SQL Server Always On Availability Group
    3分钟带你了解PowerShell发展历程——PowerShell各版本资料整理
    由Find All References引发的思考。,
  • 原文地址:https://www.cnblogs.com/stoorz/p/15542729.html
Copyright © 2011-2022 走看看