zoukankan      html  css  js  c++  java
  • P2325 [SCOI2005]王室联邦

    https://www.luogu.com.cn/problem/P2325

    据说最常用的树分块是由这题得来的。

    分块之后可以得到:每块大小大于等于(B),小于(3B)(实际上至多一个大于等于(2B))。并且对于每个块,都存在一个点(记为块顶,它不一定在块内),使得这个块的点集并这个点形成的点集联通。

    维护一个栈(st),设栈顶位置为(cur),dfs的时候到每个点记下到达这个点时候栈顶的位置(bef)

    过程:

    1. 遍历儿子(y),回溯到(x)。如果当前新加进来的大于等于(B)(cur-befge B)),则将([bef+1,cur])中的全都分成一块。
    2. (x)要回溯,把(x)加入栈顶。
    3. dfs完之后,如果栈还有剩余,就把剩下的加入最后一个块中。

    分析一下:进入子树前,栈中点最多是(B-1)。从子树出来,栈中点最多是(B)。于是总共最多为(2B-1)。栈中若有剩余最多为(B-1),加入最后一个块最多为(3B-1)


    using namespace std;
    #include <bits/stdc++.h>
    #define N 1005
    int n,B;
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v){
    	e[ne]={v,last[u]};
    	last[u]=e+ne++;
    }
    int st[N],cur;
    int bel[N],c[N],k;
    void dfs(int x,int fa){
    	int tmp=cur;
    	for (EDGE *ei=last[x];ei;ei=ei->las)
    		if (ei->to!=fa){
    			dfs(ei->to,x);
    			if (cur-tmp>=B){
    				++k;
    				while (cur>tmp)
    					bel[st[cur--]]=k;
    				c[k]=x;
    			}
    		}
    	st[++cur]=x;
    }
    int main(){
    	scanf("%d%d",&n,&B);
    	for (int i=1;i<n;++i){
    		int u,v;
    		scanf("%d%d",&u,&v);
    		link(u,v),link(v,u);
    	}
    	dfs(1,0);
    	while (cur)
    		bel[st[cur--]]=k;
    	c[k]=1;
    	printf("%d
    ",k);
    	for (int i=1;i<=n;++i)
    		printf("%d ",bel[i]);
    	printf("
    ");
    	for (int i=1;i<=k;++i)
    		printf("%d ",c[i]);
    	printf("
    ");
    	return 0;
    }
    
  • 相关阅读:
    重构与单元测试
    10个现代的软件过度设计错误
    连接ORACLE数据库,是否必须要安装oracle客户端
    关于区块链
    为什么K8s会成为主流?
    Devops K8s
    关于UDP协议
    OO第四单元总结
    OO第三单元总结--根据JML写代码
    面向对象电梯系列总结
  • 原文地址:https://www.cnblogs.com/jz-597/p/14506373.html
Copyright © 2011-2022 走看看