首页 > 其他分享 >【Centroids】不一样的解法

【Centroids】不一样的解法

时间:2024-03-07 16:35:11浏览次数:25  
标签:le int dfrac getk Centroids dfn 一样 解法 size

前言

一道好题,也就花了我一个下午而已。

本人做法比较清奇,可以当做开阔思路参考,并不太建议实操(太难调了!)。

文章较啰嗦,谅解。

思路

众所周知,我并不太喜推式子,所以我们考虑直接根据题目限定的条件硬刚。

首先,我们先假定 \(1\) 为根,并求出每个点的子树的大小,记为数组 \(size\),并且再维护一下每一个点的时间戳。

接着我们考虑一种特殊情况,即如果 \(1\) 可以为树的重心的情况,那么他就会有接下来的两种子情况:

  • 他所有的儿子的 \(size\) 全部小于等于 \(\dfrac {n}{2}\)。

  • 有一个儿子 \(x\) 的 \(size\) 大于 \(\dfrac {n}{2}\),且这个儿子的子树上一定能找到一个点 \(y\) 满足 \(size_x-size_y \le \dfrac{n}{2},size_y\le \dfrac{n}{2}\)。(把这个子树拆下来接到 \(1\) 上就可以满足重心的条件)

第二种情况不是很理解的话可以看下面这张图:

只有两种情况满足其一,那么 \(1\) 就可以成为树的重心。

接下来,我们迁移到一般的情况,那么就需要考虑除了他子树以外的情况。

对于树上的任意节点 \(x\),要让他成为树的重心,就会有接下来的三种情况。

  • 他所有的儿子的 \(size\) 全部小于等于 \(\dfrac{n}{2}\),并且 \(n-size_x \le \dfrac{n}{2}\)。

  • 同根节点的第二种情况。

  • \(n-size_x > \dfrac{n}{2}\),即删除 \(x\) 后,不属于他子树的那一部分超过了重心的限制。那么在其中,我们又要分为两种情况。首先选择一个不在 \(x\) 子树上的点 \(y\)。如果 \(y\) 不在根节点到 \(x\) 的那条链上,那么只需要满足 \(size_y \le \dfrac{n}{2},n-size_x-size_y\le \dfrac{n}{2}\)。否则,我们拆下来的,就是除了 \(y\) 子树以外的部分,接到 \(x\) 上面去(毕竟你不可以把 \(x\) 的子树也拆下来,不然就会没有任何效果。),即满足条件 \(size_y-size_z\le \dfrac{n}{2},n-size_y \le \dfrac{n}{2}\),只要找到一个符合条件的 \(y\) 即可。

那么接下来,问题就转化为了 \(x\) 能不能找到一个符合条件的 \(y\)。(毕竟第一个条件很好解决。)

接着继续转化问题,对于情况2,我们可以移一下项,将其转化为 \(y\) 满足条件 \(size_x-\dfrac{n}{2} \le size_y \le \dfrac{n}{2}\),且 \(dfn_x \le dfn_y \le dfn_x+size_x-1\)。

对于情况三的第一种情况,我们也可以移一下项,转化为 \(n-size_x-\dfrac{n}{2} \le size_y \le \dfrac{n}{2}\),且 \(dfn_y<dfn_x,dfn_y>dfn_x+size_x-1\)(对于不在链上这一限制,后面会说怎么处理)。

这两种情况都可以看做有四个常数 \(p,q,l,r\),判断是否有对于 \(x \in [p,q],y \in [l,r]\) 存在 \(x=size_y\)。

显然,我们可以对于 \(size\) 分块,然后开两个 \(set\) \(a,b\),\(a_x\) (为什么用set后面会解释。)里面放所有 \(size=x\) 的所有下标,\(b_x\) 放 \(size\) 属于 \(x\) 这个块的所有的下标。最后查询时,以 \(size\) 为第一维,对于散块,在 \(a\) 中二分,看有没有满足属于 \([l,r]\) 的值,对于整块,则在 \(b\) 中二分。

接着,我们看向比较难处理的情况三的第二种情况。

我们可以动态维护一个数组,依次储存从根到该节点的链上的所有 \(size\) 值,可以发现是单调递减。并且记得在加入数组时,删除他在 \(a,b\) 中的值,在删除数组元素时,加回去。(因为必须要保证在情况三的情况1解决时要不包含这些在链上的元素)。这里的删除和加入就解释了为什么要采用 set。接着可以先在数组中二分找出满足 \(n-size_y \le \dfrac{n}{2}\) 的 \(y\) 的范围,然后在这个范围中继续二分找出是否存在满足 \(size_y-size_z\le \dfrac{n}{2}\) 的 \(y\),那么就解决了情况3的第二种情况。

时间复杂度,分块+二分,及 \(n \sqrt n \log n\),但因为二分的区间长度不可能每次都是顶着的,所以远远达不到这个上界,但仍然很慢。

所以问题就 迎刃而解 了!

代码

#include<bits/stdc++.h>
using namespace std;
int n;
struct node
{
	int tar,nxt;
}arr[800005];
int fst[400005],cnt;
void adds(int x,int y)
{
	arr[++cnt].tar=y,arr[cnt].nxt=fst[x],fst[x]=cnt;
}
int size[400005],dfn[400005],tot,rnk[400005];
int klen,len;
set<int> a[400005],b[100005];
bool dp[400005];
void dfs(int x,int last)
{
	dfn[x]=++tot;
	rnk[dfn[x]]=x;
	for(int i=fst[x];i;i=arr[i].nxt)
	{
		int j=arr[i].tar;
		if(j==last) continue;
		dfs(j,x);
		size[dfn[x]]+=size[dfn[j]];
	} 
	size[dfn[x]]++;
}
int getk(int x)
{
	return ceil(x/(klen*1.0));
}
int lk(int x)
{
//	cout<<x<<" "<<(x-1)*klen+1<<endl;
	return (x-1)*klen+1;
}
int rk(int x)
{
	if(x==len) return n;
	else return x*klen;
}
bool ch1(int p,int x,int y)
{
//	cout<<p<<" "<<x<<" "<<y<<endl;
	set<int>::iterator q=a[p].lower_bound(x);
	if(q==a[p].end()) return false;
	else if(*q<=y) return true;
	else return false;
}
bool ch2(int p,int x,int y)
{
	set<int>::iterator q=b[p].lower_bound(x);
	if(q==b[p].end()) return false;
	else if(*q<=y) return true;
	else return false;
}
bool check(int l,int r,int x,int y)
{
//	cout<<l<<" "<<r<<" "<<x<<" "<<y<<endl;
	if(l>r) return false;
	int p=getk(x)+1,q=getk(y)-1;
	int pp=rk(getk(x)),qq=lk(getk(y));
	if(p>q)
	{
		for(int i=x;i<=y;++i) if(ch1(i,l,r)) return true;
	}
	else
	{
		for(int i=x;i<=pp;++i) if(ch1(i,l,r)) return true;
		for(int i=qq;i<=y;++i) if(ch1(i,l,r)) return true;
		for(int i=p;i<=q;++i) if(ch2(i,l,r)) return true;
	}
	return false;
}
int p[400005],tail=400001;
void add(int x)
{
	p[--tail]=size[dfn[x]];
	x=dfn[x];
	set<int>::iterator q=a[size[x]].lower_bound(x);
	a[size[x]].erase(q);
	q=b[getk(size[x])].lower_bound(x);
	b[getk(size[x])].erase(q);
}
void del(int x)
{
	tail++;
	x=dfn[x];
	a[size[x]].insert(x);
	b[getk(size[x])].insert(x);
}
void get_ans(int x,int last)
{
	if(x!=1)
	add(x);
	int flg=0,num=0,l,r;
	bool bj=0;
	for(int i=fst[x];i;i=arr[i].nxt)
	{
		int j=arr[i].tar;
		if(j==last) continue;
		if(size[dfn[j]]>n/2) flg++,num=size[dfn[j]],l=dfn[j],r=dfn[j]+size[dfn[j]]-1;
		get_ans(j,x);
	}
	del(x);
	if(n-size[dfn[x]]>n/2) flg++,num=n-size[dfn[x]],bj=1;
	if(flg>=2) return;
	if(flg==0)
	{
		dp[x]=1;
		return;
	}
	if(!bj)
	{
		if(check(l,r,num-n/2,n/2))
		dp[x]=1;
	}
	else
	{	
		//x,x+size[x]-1
		if(check(1,dfn[x]-1,num-n/2,n/2)||check(dfn[x]+size[dfn[x]],n,num-n/2,n/2))
		dp[x]=1;
		if(dp[x])
		{
			return;
		}
//		cout<<x<<" "<<1<<endl;
		int ll=lower_bound(p+tail,p+400000+1,n-n/2)-p,rr=400000,mid,ans;
		while(ll<=rr)
		{
			mid=(ll+rr)>>1;
			if(p[mid]-size[dfn[x]]<=n/2)
			{
				dp[x]=1;
				break;
			}
			else
			{
				rr=mid-1;
			}
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<n;++i)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		adds(x,y);
		adds(y,x);
	}
	klen=sqrt(n),len;
	if(klen*klen==n) len=klen;
	else len=klen+1;
	dfs(1,0);
	for(int i=1;i<=n;++i) a[size[i]].insert(i),b[getk(size[i])].insert(i);
	get_ans(1,0);
	for(int i=1;i<=n;++i) printf("%d ",dp[i]);
}
暴力出奇迹!!!!!

\[\Huge\mathscr{The\ End} \]

标签:le,int,dfrac,getk,Centroids,dfn,一样,解法,size
From: https://www.cnblogs.com/SFsaltyfish/p/18059204

相关文章

  • 244. 谜一样的牛
    题解参考AcWing244.谜一样的牛-AcWing另外,起初我以为是要对身高数组直接建立树状数组来求解问题,但是这样做的信息太少,根本不能得到答案;实际上,树状数组是用来辅助我们求身高的,我们需要构造一个树状数组,来帮助我们确认牛的身高。很多数据结构类的问题也是这样,不直接对所求问......
  • 图解法
    1当(1.2,0.2)取最小值最小值为32无解......
  • 【MySQL】【锁的前置知识】数据库的锁有哪些?怎么看?锁的是什么?什么情况下会加什么锁?什
    1 前言数据库中的锁,是一个很大的问题,从哪看起呢?该怎么看呢?所以在看锁之前,了解一些相关的前置知识,然后再去细看不同的场景下会加什么样的锁方便你快速理解。官网,当然我们这里看的引擎是InnoDB哈,那我们从以下几个问题看起:(1)数据库中的锁有哪些(怎么知道呢,网上的文章五花八门的......
  • 【算法】关于最长递增子序列(LIS)二分+贪心解法
    前言我们都知道,解决LIS的常规解法为DP,时间复杂度为O(n^2),但是在一些条件苛刻的问题中,通常DP的解法会超时。那么,是否存在一种时间复杂度更优秀的解法呢?答案是肯定的,有一种二分加贪心的解法可以将时间复杂度降低为O(nlogn)。详解如果我们现在要求下面这一组数据的LIS:我们需......
  • 软件工程师证书可以像建筑行业的证书一样给其他公司挂靠赚取收入吗?
    软件工程师证书可以像建筑行业的证书一样给其他公司挂靠赚取收入吗?建筑行业证书大部分都可以挂靠每个月也有点收入软件行业的证书可以挂靠吗到什么地方挂靠 软件设计师证书如何挂靠?如果想要把自己的软件设计师证书挂靠出去,可以自己找所需证书的企业,也可以找中介......
  • 【Python】 回文数的四种解法
    回文数就是指整数倒过来和原整数相等。1234Example1:  Input:121Output:true12345Example2:  Input:-121Output:falseExplanation:Fromlefttoright,itreads-121.Fromrighttoleft,itbecomes121-.Therefore......
  • kuhu另类解法
    escapefromwhk3题解题目大意定义两个不同质因数为kuhu,当且仅当两数和为2的整数次方.给定若干询问\([l,r]\),问在区间中取出若干的元素,使得元素两两间不为kuhu,最大的元素个数\(f(l,r)\)求\(\sum_{1\lel\ler\len}f(l,r)\)题解Part1如图,对于一个右端点,找到一个最接近它......
  • 验证对象和map赋值一样,多个方法赋值,只要中间没有重新new对象,值就会一直存在
    packageservice;importbase.BaseSpringTest;importcom.bestpay.settle.unity.certify.integration.model.CertifyInfoBO;importlombok.extern.log4j.Log4j2;importorg.junit.Test;importjava.util.HashMap;importjava.util.Map;/***场景测试类**@authorzhangkuankuan......
  • P4141 消失之物题解(写给每一位与我一样的新手玩家)
    消失之物传送门:P4141消失之物-洛谷|计算机科学教育新生态(luogu.com.cn)思路暴力稳了但是hacktle了这时候我们要想办法优化这是一个回退背包问题首先第一步,我们把正常的背包(n间物体)求出来,然后就是板子,求出填满当中体积有多少种方法第二步就是回退,回退的关键问题......
  • 【C++】判断回文字符串。回文指的是顺读和逆读都一样的字符串。例如,“tot”和“otto”
    //判断字符串是否是回文字符串(考虑大小写,空格和标点符号)boolpalindrome1(string&str){stringret;for(auto&c:str){if(isalpha(c)){if(isupper(c)){ret.push_back(tolower(c));}else{ret.push_back(c);}......