P5327 [ZJOI2019]语言

2021-03-15 13:34

阅读:687

标签:路径   int   printf   set   namespace   cto   ||   clu   cpp   

一边写草稿一边做题吧。要看题解的往下翻,或者是旁边的导航跳一下。

草稿

因为可以开展贸易活动的条件是存在一种通用语 \(L\) 满足 \(u_i\)\(v_i\) 的最短路径上都会 \(L\) 。所以我们考虑能够带来贡献的,只有同一次传教。

但是很有可能在进行当前这一次传教过程中,中间的两座城市已经可以进行贸易往来了,所以我们需要将这些部分的贡献给减去。因为是要其中都有同一种语言,所以我们只需要找连续的有相同语言的就可以了。

相当于对于一次传教,我们需要找出传教路径上的相同颜色的链的长度的城市对。这个东西搞得不好好像要变成 \(O(n^2)\)

哦,我好像会了,维护一个结构体,表示当前区间的这个答案是多少,同时从左边开始的链的颜色和长度,右边开始的链的颜色和长度,然后合并的时候就判断一下是不是相同颜色什么的就可以了。

就这?就这?


错了错了,没有这么简单。

我们需要考虑一个区间被多计算了几次,用容斥来做。我们考虑维护一个线段树,表示这个点是否被计算过,连续的一段表示这些点两两都被计算过。

我们考虑添加一条于当前表示计算过的有交的区间该怎么加,应该就直接减去这段区间中被计算过的和再加上这段区间能带来的贡献就好了吧。

这个东西应该还是可以用线段树来维护的。

就这?就这?


错了错了,我错了……

得到了一点提示,对于每一个点,若在一次染色过程中经过了该点,就在该点的集合里添加这两个点的端点,最后求每个点对应集合的最小生成树大小。

嗯,看上去可做多了,接下来考虑怎么去搞这个东西。

因为对于一次染色操作,我们是对这一条链上的所有点的集合中都塞入这两个点,所以我们可以考虑线段树合并。

那么现在就相当于我们有一个点的集合的点(存在线段树里),我们如何求他的最小生成树呢?

我们可以再开一个线段树,处理区间加,最后求有多少个节点的权值是大于 \(0\) 的。因为你每一次添加一对节点,最多在这两个节点的链上产生贡献。然后统计答案,最后除以 \(2\) 就可以了。

然后我们发现前面处理集合的这一棵线段树甚至可以不需要,只需要合并区间加的线段树就可以了吧。。。

复杂度的话区间加的线段树的操作个数是 \(O(n\log_2n)\) 的,然后每一次操作是复杂度是 \(O(\log_2n)\) 的,同时产生 \(O(\log_2n)\) 个节点。所以最终的复杂度应该是 \(O(n\log_2^2n)\) 的。

呼,终于好了。

题解

稍微理一下。

我们对于每一个,其贡献就是经过这个点的链的并。这个东西是可以用类似扫描线的东西来维护的。

然后对于两两节点之间的转移,只用线段树合并和树上差分的思路来完成的。

复杂度是 \(O(n\log_2^2n)\) 的,跑的还挺快,但是线段树合并很容易时空间假掉,需要注意!!!

代码如下

#include
using namespace std;
#define Lint long long
const int N=1e5+5;
int n,m;
struct Edge{int nxt,to;}e[Ntr[tr[u].son].size)
		tr[u].son=e[i].to;
	}
}
void dfs2(int u)
{
	dfn[++cnt]=u,mp[u]=cnt;
	if(tr[u].son)
	{
		tr[tr[u].son].top=tr[u].top;
		dfs2(tr[u].son);
	}
	for(int i=fir[u];i;i=e[i].nxt)
	{
		if(e[i].to==tr[u].fa||e[i].to==tr[u].son) continue;
		dfs2(e[i].to);
	}
}
struct Seg_Tree
{
	int rt[N],now,top,rub[N>1;
		if(xmid)
		{
			if(!tr[u].rs) tr[u].rs=newnode(),tr[tr[u].rs].alive++;
			add(tr[u].rs,mid+1,r,x,y,z);
		}
		up(u,l,r);
	}
	void del(int u)
	{
		if(tr[u].ls) tr[tr[u].ls].alive--;
		if(tr[u].rs) tr[tr[u].rs].alive--;
		if(tr[u].ls&&!tr[tr[u].ls].alive) del(tr[u].ls);
		if(tr[u].rs&&!tr[tr[u].rs].alive) del(tr[u].rs);
		recycle(u);
	}
	void merge(int u,int v,int l,int r)
	{
		tr[u].tag+=tr[v].tag;
		int mid=(l+r)>>1;
		if(tr[v].ls)
		{
			if(!tr[u].ls) tr[u].ls=newnode(),copy(tr[u].ls,tr[v].ls);
			else merge(tr[u].ls,tr[v].ls,l,mid);
		}
		if(tr[v].rs)
		{
			if(!tr[u].rs) tr[u].rs=newnode(),copy(tr[u].rs,tr[v].rs);
			else merge(tr[u].rs,tr[v].rs,mid+1,r);
		}
		up(u,l,r);
	}
}t;
void work(int rt,int u,int v,int z)
{
	while(tr[u].top!=tr[v].top)
	{
		if(tr[tr[u].top].deptr[v].dep) swap(u,v);
	t.add(rt,1,n,mp[u],mp[v],z);
}
int lca(int u,int v)
{
	while(tr[u].top!=tr[v].top)
	{
		if(tr[tr[u].top].dep
tr[v].dep) swap(u,v);
	return u;
}
vector,int> > bag[N];
Lint res=0;
void dfs(int u)//n
{
	t.rt[u]=t.newnode();
	for(int i=fir[u];i;i=e[i].nxt)
	{
		if(e[i].to==tr[u].fa) continue;
		dfs(e[i].to);
		t.merge(t.rt[u],t.rt[e[i].to],1,n);//log_2^2n
		t.del(t.rt[e[i].to]);//log_2^2n
	}
	for(int i=0;i>n>>m;
	for(int i=1,u,v;i

P5327 [ZJOI2019]语言

标签:路径   int   printf   set   namespace   cto   ||   clu   cpp   

原文地址:https://www.cnblogs.com/Point-King/p/14004669.html


评论


亲,登录后才可以留言!