P2325 [SCOI2005]王室联邦

思路

利用了树上莫队的分块方式,保证每个块的大小都(ge)B且$le$3B,然后证明略过
仅叙述一下算法的过程

使用一个栈,依次dfs这个点的每个子树,如果发现新增的节点数大于等于B,就分出新的一块,
最后把剩下的节点塞进最后一个块里

分块的代码

void dfs(int u,int f){
    int t=S.size();
    for(int i=fir[u];i;i=nxt[i]){
        if(v[i]==f)
            continue;
        dfs(v[i],u);
        if(S.size()-t>=B){
            ++block_cnt;
            core[block_cnt]=u;
            while(S.size()>t){
                belong[S.top()]=block_cnt;
                S.pop();
            }
        }
    }
    S.push(u);
}

AC代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <stack>
using namespace std;
stack<int> S;
int v[5000],fir[5000],nxt[5000],cnt,B,n,core[5000],block_cnt=0,belong[5000];
void addedge(int ui,int vi){
    ++cnt;
    v[cnt]=vi;
    nxt[cnt]=fir[ui];
    fir[ui]=cnt;
}
void dfs(int u,int f){
    int t=S.size();
    for(int i=fir[u];i;i=nxt[i]){
        if(v[i]==f)
            continue;
        dfs(v[i],u);
        if(S.size()-t>=B){
            ++block_cnt;
            core[block_cnt]=u;
            while(S.size()>t){
                belong[S.top()]=block_cnt;
                S.pop();
            }
        }
    }
    S.push(u);
}
int main(){
    scanf("%d %d",&n,&B);
    for(int i=1;i<n;i++){
        int a,b;
        scanf("%d %d",&a,&b);
        addedge(a,b);
        addedge(b,a);
    }
    dfs(1,0);
    while(!S.empty()){
        belong[S.top()]=block_cnt;
        S.pop();
    }
    printf("%d
",block_cnt);
    for(int i=1;i<=n;i++)
        printf("%d ",belong[i]);
    printf("
");
    for(int i=1;i<=block_cnt;i++)
        printf("%d ",core[i]);
    printf("
");
    return 0;
}