BZOJ 1086: [SCOI2005]王室联邦 [树上分块]
题意:
树分成若干块大小在$[s,3s]$之间,每块有一个根(可以不在块内),所有点到根路径上的点都必须在块内
据说这是一个保证了块大小直径个数的科学分块方法,貌似只有本题有用 我错了原来是树上莫队可以用啊....
做法是,dfs并维护一个栈,dfs到某一个点考虑从子树中找以它为根的块,当遍历某一棵子树结束时栈中元素$\ge s$就分成一块
可以保证块的大小$\le 2s$,因为假如这个子树结束后为$s-1$,而下一个子树最多再贡献$s$个点
处理完当前点再把当前点入栈
dfs结束后还有一些点没有块,这些点一定$<s$,直接分入最后一块就行了
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> using namespace std; typedef long long ll; const int N=1005; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int n,s; struct edge{int v,ne;}e[N<<1]; int cnt,h[N]; inline void ins(int u,int v){ e[++cnt]=(edge){v,h[u]}; h[u]=cnt; e[++cnt]=(edge){u,h[v]}; h[v]=cnt; } int st[N],top, pos[N],m,root[N]; void dfs(int u,int fa){ int bot=top; for(int i=h[u];i;i=e[i].ne) if(e[i].v!=fa) { dfs(e[i].v, u); if(top-bot>=s){ root[++m]=u; while(top!=bot) pos[st[top--]]=m; } } st[++top]=u; } int main(){ freopen("in","r",stdin); n=read();s=read(); for(int i=1;i<n;i++) ins(read(), read()); dfs(1,0); while(top) pos[st[top--]]=m; printf("%d\n",m); for(int i=1;i<=n;i++) printf("%d%c",pos[i], i==n?'\n':' '); for(int i=1;i<=m;i++) printf("%d%c",root[i],i==m?'\n':' '); return 0; }
Copyright:http://www.cnblogs.com/candy99/