【Centroids】不一样的解法
前言
一道好题,也就花了我一个下午而已。
本人做法比较清奇,可以当做开阔思路参考,并不太建议实操(太难调了!)。
文章较啰嗦,谅解。
思路
众所周知,我并不太喜推式子,所以我们考虑直接根据题目限定的条件硬刚。
首先,我们先假定
接着我们考虑一种特殊情况,即如果
-
他所有的儿子的
全部小于等于 。 -
有一个儿子
的 大于 ,且这个儿子的子树上一定能找到一个点 满足 。(把这个子树拆下来接到 上就可以满足重心的条件)
第二种情况不是很理解的话可以看下面这张图:
只有两种情况满足其一,那么
接下来,我们迁移到一般的情况,那么就需要考虑除了他子树以外的情况。
对于树上的任意节点
-
他所有的儿子的
全部小于等于 ,并且 。 -
同根节点的第二种情况。
-
,即删除 后,不属于他子树的那一部分超过了重心的限制。那么在其中,我们又要分为两种情况。首先选择一个不在 子树上的点 。如果 不在根节点到 的那条链上,那么只需要满足 。否则,我们拆下来的,就是除了 子树以外的部分,接到 上面去(毕竟你不可以把 的子树也拆下来,不然就会没有任何效果。),即满足条件 ,只要找到一个符合条件的 即可。
那么接下来,问题就转化为了
接着继续转化问题,对于情况2,我们可以移一下项,将其转化为
对于情况三的第一种情况,我们也可以移一下项,转化为
这两种情况都可以看做有四个常数
显然,我们可以对于
接着,我们看向比较难处理的情况三的第二种情况。
我们可以动态维护一个数组,依次储存从根到该节点的链上的所有
时间复杂度,分块+二分,及
所以问题就 迎刃而解 了!
代码
#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]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】