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都是独立的,然后根据下面的规则来放置:
- 先放置轻子节点,后放置重子节点。
- 轻子节点放置于父节点的上一层,重子节点放置于父节点的同一层。
然后来证明这个方法不会超过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;
}