首页 > 其他分享 >P5327 [ZJOI2019]语言 解题报告

P5327 [ZJOI2019]语言 解题报告

时间:2024-04-10 10:57:37浏览次数:25  
标签:P5327 val rs int 100005 fa 解题 ZJOI2019 id

oj:https://gxyzoj.com/d/gxyznoi/p/P35

树上差分+线段树合并+树链剖分

1. 暴力

从最基础的方法开始,暴力统计s-t上的点,然后用map直接统计,显然会T,20pts

2. 特殊性质

测试点5,6保证了数据是一条链,将链上每一个节点编号为1-n

对于每一次操作,可以记录其覆盖情况,设共有x个节点,有y个节点已被覆盖,则答案需增加\((x-1)\times(x-y)\)

如上操作,显然可以用线段树维护覆盖情况

3. 正解

利用虚树思想,若一个节点v可以与u进行贸易,则从u向v连一条边,则这棵生成树的大小-1就是可以与u进行贸易的节点个数

可以将链的方法迁移到树上

首先,对原树求dfs序,然后对于每次操作进行树剖,将每一次操作分成一些连续的区间

再考虑如何用线段树维护,转变一下思维,可以将记录是否被覆盖的数组改为记录覆盖次数

所以可以利用树上差分,在s,t处对所有区间分别加一,再在\(fa_lca\)处减去即可

然后考虑如何转移,对于每个节点,连接状态显然分为两部分

一部分是该点本身的权值,直接将预处理好的区间相加即可

另一部分是由儿子转移而来,这部分可以使用线段树合并解决

最后统计答案,每次转移后的的个数显然就是覆盖次数不为0的点数,可以另开一个数组val记录

注意,要用动态开点线段树,数组尽量开到n的128倍以上,不要写一些奇怪的语句

4. 代码

#include<cstdio>
#include<vector>
#include<algorithm>
#define ll long long
using namespace std;
int n,m,head[100005],edgenum;
struct edge{
	int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
int dep[100005],son[100005],size[100005],top[100005],fa[100005];
struct node{
	int l,r,val;
};
struct node1{
	int l,r;
};
vector<node> a[100005];
vector<node1> tmp;
void dfs(int u,int f)
{
	size[u]=1;
	fa[u]=f;
	dep[u]=dep[f]+1;
	int maxn=-1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[u]) continue;
		dfs(v,u);
		size[u]+=size[v];
		if(maxn<size[v])
		{
			maxn=size[v];
			son[u]=v;
		}
	}
}
int dfn[100005],idx,pre[100005];
void dfs2(int u,int t)
{
//	printf("%d %d\n",u,t);
	dfn[u]=++idx;
	top[u]=t;
	pre[idx]=u;
	a[u].push_back((node){dfn[u],dfn[u],1});
	if(fa[u])
	{
		a[fa[u]].push_back((node){dfn[u],dfn[u],-1});
	}
	if(son[u])
	{
		dfs2(son[u],t);
	}
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[u]||v==son[u]) continue;
		dfs2(v,v);
	}
}
int lca(int u,int v)
{
	while(top[u]!=top[v])
	{
		if(dep[top[u]]<dep[top[v]]) swap(u,v);
		tmp.push_back((node1){dfn[top[u]],dfn[u]});
		u=fa[top[u]];
	}
	if(dep[u]>dep[v]) swap(u,v);
	tmp.push_back((node1){dfn[u],dfn[v]});
	return u;
}
ll lazy[12800005];
int val[12800005],ls[12800005],rs[12800005];
void pushup(int id,int l,int r)
{
	if(lazy[id]>0) val[id]=r-l+1;
	else val[id]=val[ls[id]]+val[rs[id]];
}
int cnt;
int update(int id,int l,int r,int ql,int qr,int v)
{
	if(!id) id=++cnt;
	if(l>=ql&&r<=qr)
	{
		lazy[id]+=v;
		pushup(id,l,r);
		return id;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)
	ls[id]=update(ls[id],l,mid,ql,qr,v);
	if(qr>mid)
	rs[id]=update(rs[id],mid+1,r,ql,qr,v);
	pushup(id,l,r);
	return id;
}
int merge(int p,int q,int l,int r)
{
	if(p==0||q==0) return p+q;
	lazy[p]+=lazy[q];
	if(l==r)
	{
		pushup(p,l,r);
		return p;
	}
	int mid=(l+r)>>1;
	ls[p]=merge(ls[p],ls[q],l,mid);
	rs[p]=merge(rs[p],rs[q],mid+1,r);
	pushup(p,l,r);
	return p;
}
ll ans;
int rt[100005];
void solve(int u)
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(v==fa[u]) continue;
		solve(v);
		rt[u]=merge(rt[u],rt[v],1,n);
	}
	for(int i=0;i<a[u].size();i++)
	{
		rt[u]=update(rt[u],1,n,a[u][i].l,a[u][i].r,a[u][i].val);
	}
	ans=ans+val[rt[u]];
}
int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
		rt[i]=i;
	}
	rt[n]=n;
	cnt=n;
	dfs(1,0);
//	printf("1");
	dfs2(1,1);
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		int lc=lca(u,v);
		for(int j=0;j<tmp.size();j++)
		{
			int l1=tmp[j].l,r1=tmp[j].r;
			if(l1>r1) swap(l1,r1);
			a[u].push_back((node){l1,r1,1});
			a[v].push_back((node){l1,r1,1});
			if(fa[lc]) a[fa[lc]].push_back((node){l1,r1,-2});
		}
		tmp.clear();
	}
	solve(1);
	printf("%lld",(ans-n)/2);
	return 0;
}

标签:P5327,val,rs,int,100005,fa,解题,ZJOI2019,id
From: https://www.cnblogs.com/wangsiqi2010916/p/18125561

相关文章

  • 第十一届蓝桥杯C/C++组C组决赛之思维风暴 快速解题
    十五届蓝桥杯即将开赛,十一届的蓝桥杯国赛的一些巧妙解法。美丽的2 题目描述本题为填空题,只需要算出结果后,在代码中使用输出语包将所填结果输出即可。小蓝特别喜欢2,今年是公元2020年,他特别高兴。他很好奇,在公元1年到公元2020年(包含)中,有多少个年份的数位中包含数字2?......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧015-Explore Beautiful Lake Charle
    PDF格式公众号回复关键字:ZKYD015阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文章......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧014-A Fun Plant Experiment to Try
    PDF格式公众号回复关键字:ZKYD014阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文章......
  • volatility内存取证问题,命令总结,解题思路汇总
    volatility内存取证的简单用法**可以使用kali,windows管理员权限运行.exe程序**一、常用命令格式命令格式:volatility-f文件名--profile=dump的系统版本命令volatility-fwin7.rawimageinfo##检测目标系统信息volatility-fwin7.raw--profile=Win7SPIx64pslist##......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧013-dearMars Project
    PDF格式公众号回复关键字:ZKYD013阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文章......
  • 【解题报告】RbOI Round 1,A&D题解
    【RbOIR1】A&D题解其他题题解请移步B、C点击图片跳转比赛:A.AnxiousRobin从左到右扫一遍,按照题意模拟即可。这里解释一下我的代码:因为只判断了六种字母,所以遇到-会直接过滤,无需特判。展开代码#include<bits/stdc++.h>#definelllonglong#defineMyWifeCrista......
  • 并查集解题思路
    概述并查集主要是解决以下几种问题的:各节点之间的关系某节点和它祖先之间的关系种类朴素并查集,一个集合的信息可以存储在它的祖先节点上。带权并查集,维护的是某节点与它祖先的关系。扩展域并查集(种类并查集),本质是多开几倍空间的朴素并查集,维护的是各个阵营之间的关系,且......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧012-Instructions for Daily Use
    PDF格式公众号回复关键字:ZKYD012阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文章......
  • 手把手教你做阅读理解题-初中中考阅读理解解题技巧011-Noticeboard
    PDF格式公众号回复关键字:ZKYD011阅读理解技巧,在帮助读者有效获取和理解文本信息方面发挥着重要作用,熟练掌握如下6个技巧,可快速突破阅读理解1预览文章结构在开始深入阅读之前,快速浏览文章的标题、段落开头和结尾,可以迅速把握文章的主题、大致内容和结构标题通常能概括文章......
  • AGC008E Next or Nextnext 解题报告
    \(\text{分析}\)\(i\toa_i\)构成内向基环树,配合暴力程序观察内向基环树常见的一些特殊情况:灰色笔对应的是\(i\toa_i\),黑色笔对应的是\(i\top_i\),我们相当于要构造一个黑色的排列(若干环)使得每一条灰色边的起点可以通过一条或两条黑色边到达终点。\(a_i=i\)(全是自环):可以任......