cdcq

梦幻小鱼干

导航

【墨西哥区域赛】Carpet

原题:

题意:

给你一个树,有1e5个节点,让你把这个树放在一个长1e6宽20的网格图里,要求一个格子放一个节点,树边之间不能相交

 

这是一道构造题

因为树的形状可能性很多,很复杂,所以不能简单猜测,而必须要依据某种性质,来保证生成的解一定合法

先尝试小规模,或特殊的问题也是一个重要的思想方法

首先考虑一个节点A和它的所有儿子

不难发现,这时可以把这个节点放在某层,然后把它的儿子放在下一层的一个区间内

只要爸爸出现的顺序和儿子所在区间出现的顺序相同,就不会发生冲突

因为在两层之间连边,斜率只会无限趋于0,所以不会出现于(xA, yA+1)和(xA, yA-1)冲突的情况,只需保证这两层之间的边不相交

继而可以发现,只要满足这个条件,不管A和它的儿子们在什么位置,都不会和别人冲突

为了压缩空间,所有的儿子可以连续放在一起,即使和爸爸相距太远也没有问题

其次,题目数据中宽度为20

根据经验,与众不同的数字往往有重要含义

可以敏感地发现20刚好是log1e6

联系到树链剖分

因为重链剖分保证任意一个节点到根节点的路径最多只会经过logn条重链

所以可以让一条重链躺在一层,而轻儿子都放到下一层,根节点放左上角

这样对于任意一点,想要通过构造的网格图去往根节点,至多会往上爬logn层

所以深度不会超过限制,而宽度显然充足

所以问题就解决了

轻儿子放在连续的区间,区间出现的顺序和爸爸出现的顺序相同,这能保证不发生冲突

进行树链剖分,重链剖分的性质保证深度不会超过限制

 

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int rd(){int z=0;  char ch=getchar();
 8     while(ch<'0'||ch>'9')  ch=getchar();
 9     while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0';  ch=getchar();}
10     return z;
11 }
12 struct edg{int y,nxt;}e[210000];  int lk[110000],ltp=0;
13 void ist(int x,int y){
14     e[++ltp]=(edg){y,lk[x]};  lk[x]=ltp;
15     e[++ltp]=(edg){x,lk[y]};  lk[y]=ltp;
16 }
17 int n;
18 int fth[110000],hvc[110000],sz[110000];
19 int ax[110000],ay[110000];
20 int hd[30];
21 void dfs1(int x,int y){
22     fth[x]=y;  sz[x]=1;
23     int mx=0,mxid=0;
24     for(int i=lk[x];i;i=e[i].nxt)if(e[i].y!=y){
25         dfs1(e[i].y,x);
26         sz[x]+=sz[e[i].y];
27         if(sz[e[i].y]>mx)  mx=sz[e[i].y],mxid=e[i].y;
28     }
29     hvc[x]=mxid;
30 }
31 void dfs(int x,int y){
32     ay[x]=y,ax[x]=++hd[y];
33     for(int i=lk[x];i;i=e[i].nxt)if(e[i].y!=fth[x] && e[i].y!=hvc[x])
34         dfs(e[i].y,y+1);
35     if(hvc[x])  dfs(hvc[x],y);
36 }
37 int main(){
38     //freopen("ddd.in","r",stdin);
39     memset(lk,0,sizeof(lk));
40     memset(hd,0,sizeof(hd));
41     cin>>n;
42     int l,r;
43     for(int i=1;i<n;++i){
44         l=rd(),r=rd();
45         ist(l,r);
46     }
47     dfs1(1,0);
48     dfs(1,1);
49     for(int i=1;i<=n;++i)  printf("%d %d\n",ax[i],ay[i]);
50     return 0;
51 }
View Code

 

posted on 2019-09-21 20:51  cdcq  阅读(166)  评论(0编辑  收藏  举报