题目连接:D. Nastya and Time Machine
题目大意:给你一棵树,通过每条边需要(1)的时间,你可以在一个结点处将时间变为任意一个比当前时间小的非负整数,要求满足以下两个限制:
- 经过每个节点至少一次
- 每一个节点上,到达它的时间(更改时的时间也计算在内)不得重复
求出任意一种使得到达每个节点的时间最大值最小。
题解:最大最小,直接二分答案上啊。要求出一种方案,这题二分答案不大好做,考虑直接构造。
首先考虑单纯的走(即不考虑修改时间),每个节点很明显经过的次数是它的度数次,并且,如果考虑修改时间,若修改一次时间不算经过该节点一次的话,那么经过该节点的时间肯定是度数次(多了只会给自己加负担)。
那么先考虑确定下答案,二分答案多好啊(博主这个菜鸡只会二分答案了),首先,很明显最终答案肯定不小于每个点的度数中的最大值,所以答案就是每个点的度数的最大值减一(因为从(0)开始)……吗?样例就推翻了我们的这个结论。
仔细考虑,每个点很明显最小是它的度数减一,但是问题在于如果进入这个点时的时间不为(0),那么就需要更改一次时间,但是更改时间又会导致当前点时间个数的浪费,所以更改时间自然是越少越好,但因为更改时间不可避免,所以考虑每一个点能否最多只用一次更改时间,这样的话答案就是每个点的度数的最大值。
令(maxdeg=max_{u} deg_u ext{(} deg_u ext{表示节点}u ext{的度数)}),那么考虑让每一个点的时间取值为([maxdeg-deg_u,maxdeg]),一共有(deg_u+1)中取值,满足我们的要求。
接下来就是考虑怎么构造答案了,因为我们钦定每一个节点的时间取值是在一个范围内,所以到儿子的时间就要连续(将该区间看成一个环),因为只能更改一次时间,这可以在当前时间到达(maxdeg)时更改为(maxdeg-deg_u),然后这题就做完了(有一些简单的情况就不列了)。
下面是代码:
#include <cstdio>
void read(int &a){
a=0;
char c=getchar();
while(c<'0'||c>'9'){
c=getchar();
}
while(c>='0'&&c<='9'){
a=(a<<1)+(a<<3)+(c^48);
c=getchar();
}
}
int max(int a,int b){
return a>b?a:b;
}
const int Maxn=100000;
int n,ans;
int fa[Maxn+5];
int head[Maxn+5],arrive[Maxn<<1|5],nxt[Maxn<<1|5],tot;
void add_edge(int from,int to){
arrive[++tot]=to;
nxt[tot]=head[from];
head[from]=tot;
}
int deg[Maxn+5];
struct Operation{
int u,t;
}op[Maxn<<2|5];
int len;
void work_dfs(int u,int t){
op[++len].u=u;
op[len].t=t;
int last=t;
bool flag=0;
if(last==ans){
last=ans-deg[u];
flag=1;
}
for(int i=head[u];i;i=nxt[i]){
int v=arrive[i];
if(v==fa[u]){
continue;
}
fa[v]=u;
if(flag){
op[++len].u=u;
op[len].t=last;
flag=0;
}
work_dfs(v,++last);
op[++len].u=u;
op[len].t=last;
if(last==ans){
last=ans-deg[u];
flag=1;
}
}
if(flag){
last=ans;
}
if(u!=1&&last!=t-1){
op[++len].u=u;
op[len].t=t-1;
}
}
int main(){
read(n);
for(int i=1;i<n;i++){
int u,v;
read(u),read(v);
add_edge(u,v);
add_edge(v,u);
deg[u]++,deg[v]++;
}
for(int i=1;i<=n;i++){
ans=max(ans,deg[i]);
}
work_dfs(1,0);
printf("%d
",len);
for(int i=1;i<=len;i++){
printf("%d %d
",op[i].u,op[i].t);
}
return 0;
}