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;
    }
    
  • 相关阅读:
    ZOJ 1002 Fire Net
    Uva 12889 One-Two-Three
    URAL 1881 Long problem statement
    URAL 1880 Psych Up's Eigenvalues
    URAL 1877 Bicycle Codes
    URAL 1876 Centipede's Morning
    URAL 1873. GOV Chronicles
    Uva 839 Not so Mobile
    Uva 679 Dropping Balls
    An ac a day,keep wa away
  • 原文地址:https://www.cnblogs.com/stoorz/p/15542729.html
Copyright © 2011-2022 走看看