C Carpet

10.27训练题目笔记

Gym - 101611C Carpet

题目大意

给你一个n(1<=n<=1e5),表示树的结点个数为n,接下来n-1行输入ui和vi,表示结点u和结点v之间有一条边,现在需要你把这棵树放在一个x*y的矩形中(1<=x<=1e6,1<=y<=20),且没有边交叉。然后输出树上的每个结点在矩形中的坐标。

分析

显然对于结点个数来说,x是一个极度富裕的值,而y是个珍稀值。
那么我们很容易就想到将这个树根据树的深度,选择纵向延展或者是横向延展。
以深度20为分界线。
#include<bits/stdc++.h>

using namespace std;

const int maxn = 1e6+10;

typedef long long ll;

struct node{
    int next, to;
}mp[maxn];
int head[maxn],cnt=0;
int dep[maxn],vis[maxn]={0};
int Y[maxn],X[maxn];//X和Y中存着对应下标的深度能放结点的位置
int y[maxn],x[maxn];//x和y中存的是下标对应的位置
int max_dep=0;
void add(int u,int v){
    mp[++cnt].next = head[u];
    mp[cnt].to = v;
    head[u] = cnt;
}
//第一次深搜跑出最大的深度和各结点的深度
void dfs(int n){
    vis[n]=1;
    max_dep=max(max_dep,dep[n]);
    for(int i=head[n];i;i=mp[i].next){
        int To = mp[i].to;
        if(!vis[To]){
            dep[To] = dep[n] + 1;
            dfs(To);
        }
    }
}
//纵向延展时x的值
void dfsx(int n){
    vis[n] = 1;
    x[n] = X[dep[n]];
    X[dep[n]]++;
    for(int i = head[n]; i; i = mp[i].next){
        int To = mp[i].to;
        if(!vis[To]){
            dfsx(To);
        }
    }
}
//横向延展时y的值
void dfsy(int n){
    vis[n] = 1;
    y[n] = Y[dep[n]];
    Y[dep[n]]++;
    for(int i = head[n]; i; i = mp[i].next){
        int To = mp[i].to;
        if(!vis[To]){
            dfsy(To);
        }
    }
    return ;
}

int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
        Y[i] = 1;
        X[i] = 1;
    }
    dep[1] = 1;
    dfs(1);
    memset(vis,0,sizeof(vis));
    if(max_dep<=20){
        dfsx(1);
        for(int i = 1; i <= n; i++){
            printf("%d %d\n",x[i],dep[i]);
        }
    }
    else{
        dfsy(1);
        for(int i = 1; i <= n; i++){
            printf("%d %d\n",dep[i],y[i]);
        }
    }
    return 0;
}

这个思路是很容易想到的,但是对于1<=n<=1e5来说,可以很轻易的构造出一深度大于20,且同一深度大于20个结点的树。
所以理所当然的wa3了
这时候就想到了树剖。
我们知道重链剖分可以将树分为重子树和轻子树,而轻子树的结点个数一定小于n/2.
我们可以把1看做根放到(1,1),然后上面提到过x的值是十分富裕的,我们就可以保证每个结点的x都是独立的,然后根据下面的规则来放置:

  1. 先放置轻子节点,后放置重子节点。
  2. 轻子节点放置于父节点的上一层,重子节点放置于父节点的同一层。

然后来证明这个方法不会超过y的上限。
我们知道剖分之后可以分为重子节点所在的子树-重子树,和轻子节点所在的子树-轻子树。同时轻子树的结点个数一定是比n/2要小的。那么根据上述方法去放置的时候,只有当遇到轻子节点的时候才会向上放,那么如果向上放置超过最大的y,就需要满足n> 2^20>1e6,而n最大为1e5,所以它是不会超了y的上限的。
AC代码

#include <bits/stdc++.h>

using namespace std;

const int maxn = 1e6+100;

typedef long long ll;

int siz[maxn],hson[maxn];
int head[maxn],cnt=0;
void init(){
    memset(head,0,sizeof(head));
}
struct node{
    int to,next;
}mp[maxn];
void add(int u,int v){
    mp[++cnt].next=head[u];
    mp[cnt].to=v;
    head[u]=cnt;
}
//对于这道题来说剖分只需要把子树大小和重子节点跑出来即可
void dfs1(int f,int u){
    hson[u] = -1;
    siz[u] = 1;
    for(int i=head[u];i;i=mp[i].next)
    {
        int To=mp[i].to;
        if(To == f) continue;
        dfs1(u,To);
        siz[u]+=siz[To];
        if(hson[u]==-1||siz[To]>siz[hson[u]])
            hson[u]=To;
        
    }
}
int x[maxn],y[maxn];
int tot = 0;
void dfs2(int f,int u,int t){
    x[u] = ++tot;
    y[u] = t;
    if(hson[u]==-1) return;
    for(int i = head[u]; i;i = mp[i].next){
        int To = mp[i].to;
        if(To == f || To == hson[u]) continue;
        dfs2(u,To,t+1);
    }
    dfs2(u,hson[u],t);
}
int main(){  
    int n;
    scanf("%d",&n);
    init();
    for(int i = 1; i < n; i++){
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    dfs1(0,1);
    dfs2(0,1,1);
    for(int i = 1; i <= n; i++)
        printf("%d %d\n",x[i],y[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值