P2325 [SCOI2005]王室联邦 - 树分块

P2325 [SCOI2005]王室联邦

传送门

题意:

将一棵树分为大小在\([B,3B]\)之间的块,每个块有一个编号\(i\),每种块\(i\)有一个关键点\(P\)(关键点\(P\)可以不再块\(i\)中),所有编号为\(i\)的块内的所有点到\(P\)的路径上不存在其他编号不为\(i\)的块内的点(除关键点\(P\))。其中,\(P\)可以为多种编号的块的关键点。问将这棵树划分的方案。

思路:

树分块。
对树进行DFS,用一个栈保存遍历到的节点,记录对\(u\)的子树\(v\)DFS前后top的差值\(\Delta\),若\(\Delta \geq B\),则将栈中新加入的\(\Delta\)个元素分为同一个块,然后令\(u\)为这个块的关键点。最后DFS结束后将栈内剩余的节点加入最后一个块中。这样分块能够满足题目的所有条件。
关于算法正确性:每次的\(\Delta\)\([B,2B]\)之间,而最后剩余的栈内元素一定在\([0,B)\)之间,所以最后一个块的大小不会超过\([2B,3B)\)个,保证合法。

AC Code:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1000+100;
int n,b;
struct node{
    int to,nxt;
}e[N<<1];
int head[N],tot;
void add(int u,int v){
    e[++tot].to=v;e[tot].nxt=head[u];head[u]=tot;
}
int s[N],top;
int col[N],sh[N],cnt;
void dfs(int u,int fa){
    int now=top;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==fa) continue ;
        dfs(v,u);
        if(top-now>=b) {
            sh[++cnt]=u;
            while(top!=now) col[s[top--]]=cnt;
        }
    }
    s[++top]=u;
}
int main(){
    scanf("%d%d",&n,&b);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    dfs(1,0);
    while(top) col[s[top--]]=cnt;
    printf("%d\n",cnt);
    for(int i=1;i<=n;i++) printf("%d%c",col[i],i==n?'\n':' ');
    for(int i=1;i<=cnt;i++) printf("%d%c",sh[i],i==n?'\n':' ');
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Loi-Brilliant/p/9612438.html