1. 程式人生 > >BZOJ1086: [SCOI2005]王室聯邦

BZOJ1086: [SCOI2005]王室聯邦

com pri 不能 找到 font 不知道 line href sin

【傳送門:BZOJ1086】


簡要題意:

  給出n個點,n-1條邊,將這n個點分成若幹個部分,每個部分都有一個中心,給出B,要求2B>=每個部分的點數>=B,是每個部分中的任意一個點到達中心所經過的點(除了最後一個點,即該部分的中心點)都必須屬於該部分,求出是否能滿足將n個點都分成若幹個部分


題解:

  既然題目要求該區域所有的點到根的路徑上的點都屬於該區域。很明顯,就是一棵樹,所以題意其實就是把一棵多叉樹分成若幹個區域,然而又不用輸出最小方案,所以弱爆了。這個玩意我也不知道它算什麽,應該屬於dfs的範疇吧。

  一種簡單粗暴的想法就是dfs,每找到B個就分一塊,但是這樣連通性不能保證(一顆子樹的下半截和另一棵子樹的上半截組成一塊)。所以我們就想:能不能從底部往上組塊,每棵子樹較深的部分自己成塊,然後靠近根的部分組成一個大塊。

  所以我們這麽做:對於一個點x,以初次訪問它時,棧的棧頂作為相對棧底,每遍歷完它的一個子節點所在的子樹,判斷此時棧頂-相對棧底得到的元素個數是否大於或等於b,若成立,那麽彈棧至相對棧頂。當訪問完所有子節點要回溯到x的父節點時,把x壓入棧。

  這樣就可以保證連通性和塊大小不會超了,最後dfs結束後肯定還會有剩余的未組成塊的節點,把它們歸到最後一個塊就可以了。


參考代碼:

#include<cstdio>
#include<cstring>
using namespace std;
struct node
{
    int x,y,next;
}a[
2100];int len,last[1100]; void ins(int x,int y) { len++; a[len].x=x;a[len].y=y; a[len].next=last[x];last[x]=len; } int sta[1100],top; int belong[1100],tot; int rt[1100],n,b; void dfs(int fa,int x) { int now=top; for(int k=last[x];k;k=a[k].next) { int y=a[k].y;
if(y!=fa) { dfs(x,y); if(top-now>=b) { rt[++tot]=x; while(top!=now) { belong[sta[top--]]=tot; } } } } sta[++top]=x; } int main() { scanf("%d%d",&n,&b); if(n<b){printf("0\n");return 0;} for(int i=1;i<n;i++) { int x,y;scanf("%d%d",&x,&y); ins(x,y);ins(y,x); } dfs(0,1); while(top) belong[sta[top--]]=tot; printf("%d\n",tot); for(int i=1;i<=n;i++)printf("%d ",belong[i]); printf("\n"); for(int i=1;i<=tot;i++)printf("%d ",rt[i]); printf("\n"); return 0; }

BZOJ1086: [SCOI2005]王室聯邦