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;
}
posted @ 2021-04-30 16:09  GK0328  阅读(112)  评论(0编辑  收藏  举报