UOJ618 【JOISC2021】聚会 2
UOJ618 【JOISC2021】聚会 2
有根树点分治
先分析一下题目,对于一次会议来说,我们把会议的出席者权值定义为\(1\),那么满足条件的岛一定是树的带权重心。
经过分析(手模)可以发现,如果总权值\(V\)为奇数,那么答案为\(1\)。如果权值\(V\)为偶数,可能会出现一条全\(0\)链,链的两端子树权值为\(\frac{V}{2}\),那么整条全\(0\)链以及两端的子树的根节点都是合法的。
那么对于一个偶权值\(V\)来说,我们需要找到一条长度最大的链,满足链的两端子树大小均\(\ge \frac{V}{2}\)。
\(\ge\)并不容易,但是我们可以把它直接变成\(=\),然后一遍后缀\(\max\)。
查询所有链信息,很容易想到点分治,问题在于如何处理子树大小,显然这里的子树大小不仅仅是钦定一个根情况下的子树大小。
我们可以先确定一个根,在有根情况下计算出每棵子树的大小,对于点分中心\(u\)来说,向原有根树父亲方向的那条链,子树大小需要修改。然后枚举较小的子树大小,在其他的\(u\)子树内查询比它子树大小更大的那些节点中,深度的最大值,由于需要去重,写了个线段树叶子节点套\(\operatorname{multiset}\)的结构。需要注意的是,\(u\)节点与其他节点连成的链的答案需要单独计算,对于祖先节点及其子树和子孙节点,\(u\)对应的子树大小是不同的。
然后就做完力!
时间复杂度:\(O(n \log^2 n)\)。
\(update:\)由于常数太大,洛谷时限较小被卡,把线段树叶子节点套\(\operatorname{multiset}\)的结构换成了两次线段树,这份代码也会在下面贴出。
线段树叶子节点套\(\operatorname{multiset}\)的结构:
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<set>
#define N 200005
using namespace std;
const int INF=1000000007;
int n,x,y,ans[N];
int rtsz,rt,lsz;
struct edge
{
int nxt,v;
edge () {}
edge (int Nxt,int V):nxt(Nxt),v(V) {}
}e[N << 1];
int tot,fr[N],sz[N],fa[N],cz[N],tz[N],dep[N];
bool vis[N];
void add(int x,int y)
{
e[++tot]=edge(fr[x],y),fr[x]=tot;
}
void dfs(int u,int F)
{
cz[u]=1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F)
continue;
fa[v]=u,dfs(v,u);
cz[u]+=cz[v];
}
}
void findrt(int u,int F,int rn)
{
int mx(-1);
sz[u]=1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v] || v==F)
continue;
findrt(v,u,rn);
sz[u]+=sz[v];
mx=max(mx,sz[v]);
}
mx=max(mx,rn-sz[u]);
if (mx<rtsz)
rtsz=mx,rt=u;
}
void getrt(int u,int rn)
{
rtsz=INF,findrt(u,0,rn);
}
int tr[N << 2];
multiset<int,greater< int > >kz[N << 2];
void update(int p)
{
tr[p]=max(tr[p << 1],tr[p << 1 | 1]);
}
void modify(int p,int l,int r,int x,int y,int z)
{
if (l==r)
{
if (z==1)
kz[p].insert(y); else
kz[p].erase(kz[p].find(y));
if (kz[p].empty())
tr[p]=0; else
tr[p]=*kz[p].begin();
return;
}
int mid(l+r >> 1);
if (x<=mid)
modify(p << 1,l,mid,x,y,z); else
modify(p << 1 | 1,mid+1,r,x,y,z);
update(p);
}
int calc(int p,int l,int r,int x,int y)
{
if (l==x && r==y)
return tr[p];
int mid(l+r >> 1);
if (y<=mid)
return calc(p << 1,l,mid,x,y); else
if (x>mid)
return calc(p << 1 | 1,mid+1,r,x,y); else
return max(calc(p << 1,l,mid,x,mid),calc(p << 1 | 1,mid+1,r,mid+1,y));
}
void toseg(int u,int F,int opt)
{
modify(1,1,n,tz[u],dep[u],opt);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
dep[v]=dep[u]+1;
toseg(v,u,opt);
}
}
void calc(int u,int F)
{
int t(calc(1,1,n,tz[u],n));
if (t)
ans[tz[u]]=max(ans[tz[u]],dep[u]+t+1);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
calc(v,u);
}
}
void calc(int u,int F,int t)
{
ans[min(tz[u],t)]=max(ans[min(tz[u],t)],dep[u]+1);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
calc(v,u,t);
}
}
void calc(int u)
{
int x(fa[u]),pre(u);
while (x && !vis[x])
tz[x]=n-cz[pre],pre=x,x=fa[x];
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
dep[v]=1,toseg(v,0,1);
}
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
dep[v]=1,toseg(v,0,-1);
calc(v,0);
toseg(v,0,1);
}
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
calc(v,0,(v==fa[u])?cz[u]:n-cz[v]);
dep[v]=1,toseg(v,0,-1);
}
x=fa[u];
while (x && !vis[x])
tz[x]=cz[x],x=fa[x];
}
void solve(int u)
{
int tsz(lsz);
vis[u]=true;
calc(u);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
lsz=(sz[u]>sz[v])?sz[v]:tsz-sz[u];
getrt(v,lsz);
solve(rt);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
memcpy(tz,cz,(n+1)*sizeof(int));
getrt(1,n),lsz=n;
solve(rt);
ans[n+1]=1;
for (int i=n;i;--i)
ans[i]=max(ans[i],ans[i+1]);
for (int i=1;i<=n;++i)
if (i & 1)
puts("1"); else
printf("%d\n",ans[i >> 1]);
return 0;
}
两次线段树:
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<vector>
#define IT vector<int> :: iterator
#define N 200005
using namespace std;
const int INF=1000000007;
int n,x,y,ans[N];
int rtsz,rt,lsz;
struct edge
{
int nxt,v;
edge () {}
edge (int Nxt,int V):nxt(Nxt),v(V) {}
}e[N << 1];
int tot,fr[N],sz[N],fa[N],cz[N],tz[N],dep[N];
bool vis[N];
void add(int x,int y)
{
e[++tot]=edge(fr[x],y),fr[x]=tot;
}
void dfs(int u,int F)
{
cz[u]=1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F)
continue;
fa[v]=u,dfs(v,u);
cz[u]+=cz[v];
}
}
void findrt(int u,int F,int rn)
{
int mx(-1);
sz[u]=1;
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v] || v==F)
continue;
findrt(v,u,rn);
sz[u]+=sz[v];
mx=max(mx,sz[v]);
}
mx=max(mx,rn-sz[u]);
if (mx<rtsz)
rtsz=mx,rt=u;
}
void getrt(int u,int rn)
{
rtsz=INF,findrt(u,0,rn);
}
int tr[N << 2];
void update(int p)
{
tr[p]=max(tr[p << 1],tr[p << 1 | 1]);
}
void modify(int p,int l,int r,int x,int y)
{
tr[p]=max(tr[p],y);
if (l==r)
return;
int mid(l+r >> 1);
if (x<=mid)
modify(p << 1,l,mid,x,y); else
modify(p << 1 | 1,mid+1,r,x,y);
update(p);
}
int calc(int p,int l,int r,int x,int y)
{
if (l==x && r==y)
return tr[p];
int mid(l+r >> 1);
if (y<=mid)
return calc(p << 1,l,mid,x,y); else
if (x>mid)
return calc(p << 1 | 1,mid+1,r,x,y); else
return max(calc(p << 1,l,mid,x,mid),calc(p << 1 | 1,mid+1,r,mid+1,y));
}
void cleanseg(int p,int l,int r)
{
if (!tr[p])
return;
tr[p]=0;
if (l==r)
return;
int mid(l+r >> 1);
cleanseg(p << 1,l,mid);
cleanseg(p << 1 | 1,mid+1,r);
}
void toseg(int u,int F)
{
modify(1,1,n,tz[u],dep[u]);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
toseg(v,u);
}
}
void calc(int u,int F)
{
int t(calc(1,1,n,tz[u],n));
if (t)
ans[tz[u]]=max(ans[tz[u]],dep[u]+t+1);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
dep[v]=dep[u]+1;
calc(v,u);
}
}
void calc(int u,int F,int t)
{
ans[min(tz[u],t)]=max(ans[min(tz[u],t)],dep[u]+1);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (v==F || vis[v])
continue;
calc(v,u,t);
}
}
void calc(int u)
{
int x(fa[u]),pre(u);
while (x && !vis[x])
tz[x]=n-cz[pre],pre=x,x=fa[x];
vector<int>V;
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
V.push_back(v);
dep[v]=1,calc(v,0),toseg(v,0);
}
cleanseg(1,1,n);
reverse(V.begin(),V.end());
for (IT it=V.begin();it!=V.end();++it)
{
int v(*it);
dep[v]=1,calc(v,0),toseg(v,0);
}
cleanseg(1,1,n);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
calc(v,0,(v==fa[u])?cz[u]:n-cz[v]);
}
cleanseg(1,1,n);
x=fa[u];
while (x && !vis[x])
tz[x]=cz[x],x=fa[x];
}
void solve(int u)
{
int tsz(lsz);
vis[u]=true;
calc(u);
for (int i=fr[u];i;i=e[i].nxt)
{
int v(e[i].v);
if (vis[v])
continue;
lsz=(sz[u]>sz[v])?sz[v]:tsz-sz[u];
getrt(v,lsz);
solve(rt);
}
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
memcpy(tz,cz,(n+1)*sizeof(int));
getrt(1,n),lsz=n;
solve(rt);
ans[n+1]=1;
for (int i=n;i;--i)
ans[i]=max(ans[i],ans[i+1]);
for (int i=1;i<=n;++i)
if (i & 1)
puts("1"); else
printf("%d\n",ans[i >> 1]);
return 0;
}