1. 程式人生 > >「清華集訓2017」榕樹之心

「清華集訓2017」榕樹之心

name clear 方向 世界 oid 可行性 pre lin size

「清華集訓2017」榕樹之心


“已經快是嚴冬了,榕樹的葉子還沒落呢……”

“榕樹是常綠樹,是看不到明顯的落葉季節的……”

“唉……想不到已經七年了呢。榕樹還是當年的榕樹,你卻不是當年的你了……”

“其實又有什麽是一成不變的呢,榕樹常綠,翠綠樹冠的宏觀永恒,是由無數細小樹葉的榮枯更叠組成的。在時間的流逝中一切都在不斷變化著呢……”

“但你看這榕樹,日日如此,季季如此,年年如此,仿佛亙古不變般,盤根錯節,郁郁蔥蔥。我在想,或許成為一棵樹更好吧,任時間從枝葉間流過,我只守這一片綠蔭就好。”

“榕樹固然長久,但在這無限的時光裏,終歸是要湮滅於塵土的。與其像榕樹一般,植根於一方泥土中感受年復一年的四季更替。倒不如在有限的時間裏看過盡可能多的世界吧。再說了,榕樹雖生長緩慢,卻依舊會在每年春天抽出一根新的枝條去向外探索的呢……”

“真的嗎,榕樹在她漫長的一生裏,就是這樣往外一步步探索的嗎?”

“畢竟就算樹冠看起來一成不變,榕樹也會隨著時間周期變化,春天到了自然就是生長的時候了,她也應當做出對應的表現吧……”

“相比於對季節更替做出本能的生長,我倒寧願相信,榕樹有一顆活躍的的,探索的心。”

“其實榕樹是有心的,榕樹剛剛種下的時候,心就在根的地方發芽了。以後每年春天榕樹長出新枝條的時候,心就會向著新枝條的方向移動一點,這樣就能更靠近外面的世界了。你看這頭頂上的枝條,縱橫交錯,其實心已經在這枝杈間,移動了數十載了呢……”

“哇,也就是說,這密密麻麻的樹杈中的某個地方,藏著這棵榕樹的心嗎?”

“沒錯,可是要知道它在哪,就得另花一番功夫了……”

“呀,這時候想想,一株樹還是不如一個人好……比如你,要是這樣貼上去的話,就能聽到跳動的聲音呢……”


解題思路

先判斷最後能否到 \(1\) 怎麽做,考慮每一個節點不同兒子子樹的兩次操作會相互抵消,顯然移回當前節點剩下最少操作的方案一定是剩下若幹來自最大兒子子樹的點,那麽只需要盡可能消除最大兒子子樹即可。

\(tot[u]\) 表示從 \(u\) 子樹內從 \(u\) 出發最後回到 \(u\) ,最少剩余的生長操作數量。
\[ \begin{cases} tot[u] = sz[u] \bmod 2, & sz[u]-sz[v]-1 \geq tot[v]+1 \tot[u]=tot[v]+1-(sz[u]-tot[v]-1), &otherwise \end{cases} \]


其中 \(v\)\(u\) 的所有兒子中子樹大小最大的那個,考慮如果最大兒子內部消完剩下的東西可以直接用其它兒子的子樹消掉,那麽答案就是 \(sz[u] \bmod 2\) ,否則每一個其它兒子子樹內的點都能和最大兒子剩下的東西消一下,直接減即可。

考慮一次性要求所有點的可行性怎麽做,考慮在移到 \(u\) 時當前已經完成的操作一定是 \(1-u\) 的路徑以及路徑上掛著的一些子樹,事實上一定存在一種策略可以讓這些掛著的子樹互相消除,那麽問題就轉化成和判斷 \(1\) 的可行性類似的問題,把 \(1-x\) 路壓縮起來當做新根即可。

code

/*program by mangoyang*/ 
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
    int ch = 0, f = 0; x = 0;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
    for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
    if(f) x = -x;
}
const int N = 1000005;
vector<int> g[N];
int sz[N], ms[N], tot[N], ans[N], n;
inline void dfs(int u, int fa){
    sz[u] = 1;
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i];
        if(v == fa) continue;
        dfs(v, u), sz[u] += sz[v];
        if(sz[v] > sz[ms[u]]) ms[u] = v;
    }
    if(!ms[u]) return; 
    if(tot[ms[u]] + 1 <= sz[u] - sz[ms[u]] - 1)
        tot[u] = ((sz[u] - 1) & 1);
    else tot[u] = tot[ms[u]] + 1 - (sz[u] - sz[ms[u]] - 1);
}
inline void dfs2(int u, int fa, int anc, int add){
    int ms2 = 0;
    if(u > 1){
        int size = sz[u] + add, mx;
        if(sz[ms[u]] > sz[anc]) mx = ms[u]; else mx = anc;
        if(tot[mx] + 1 <= size - sz[mx] - 1) ans[u] = ((size - 1) & 1);
        else ans[u] = tot[mx] + 1 - (size - sz[mx] - 1);
    }
    for(int i = 0; i < (int) g[u].size(); i++)
        if(g[u][i] != fa && g[u][i] != ms[u] && sz[g[u][i]] > sz[ms2]) ms2 = g[u][i];
    for(int i = 0; i < (int) g[u].size(); i++){
        int v = g[u][i], tmp = 0;
        if(v == fa) continue;
        if(v == ms[u]) tmp = sz[ms2] > sz[anc] ? ms2 : anc;
        else tmp = sz[ms[u]] > sz[anc] ? ms[u] : anc;
        dfs2(v, u, tmp, add + sz[u] - sz[v] - 1);
    }
}
int main(){
    int T, type; read(type), read(T); while(T--){
        read(n);
        for(int i = 1; i <= n; i++) g[i].clear(), sz[i] = tot[i] = ms[i] = ans[i] = 0;
        for(int i = 1, x, y; i < n; i++){
            read(x), read(y);
            g[x].push_back(y), g[y].push_back(x);
        }
        dfs(1, 0);
        ans[1] = tot[1];
        dfs2(1, 0, 0, 0);
        if(type == 3){ printf("%d\n", !ans[1]); continue; }
        for(int i = 1; i <= n; i++) putchar(!ans[i] ?'1':'0');
        puts("");
    }
    return 0;
}   

「清華集訓2017」榕樹之心