CSP2019 樹上的數

2022-06-29 11:42:13 字數 2908 閱讀 5737

給定乙個大小為 \(n\) 的樹,它共有 \(n\) 個結點與 \(n-1\) 條邊,結點從 \(1\sim n\) 編號。初始時每個結點上都有乙個 \(1\sim n\) 的數字,且每個 \(1\sim n\) 的數字都只在恰好乙個結點上出現。

接下來你需要進行恰好\(n-1\) 次刪邊操作,每次操作你需要選一條未被刪去的邊,此時這條邊所連線的兩個結點上的數字將會交換,然後這條邊將被刪去。

\(n-1\) 次操作過後,所有的邊都將被刪去。此時,按數字從小到大的順序,將數字 \(1\sim n\) 所在的結點編號依次排列,就得到乙個結點編號的排列 \(p_i\)。現在請你求出,在最優操作方案下能得到的字典序最小的 \(p_i\)。

資料範圍:\(1\le t\le 10, 1\le n\le 2000\)。

我們考慮貪心,從小到大列舉 \(n\) 個數,假設列舉到第 \(k\) 個數(此時已經確定 \(p_\)),找到編號最小的節點 \(u\),讓 \(k\) 去 \(u\),並保證存在一種方案能夠使 \(p_\) 合法

接下來我們需要考慮怎麼保證這件事情。我們從每條邊被刪去的時間考慮,考慮下述例子:

如果②去點2,我們能夠知道:\((1,4)\) 是 \((1,3),(1,4)\) 中最先被刪的(不然②就去別的地方了比如3),對於 4 的鄰接表 \(\\) 這三條邊,\((4,1)\) 被刪後**馬上跟著 **\((4,2)\) (不然②就去別的地方了比如5)。如果 2 的鄰接表不止 \((2,4)\) 乙個,我們還能確定 \((2,4)\) 是 2 的鄰接表中最先被刪的(不然②不可能停留在2)。

那麼滿足這些條件的所有方案是不是都能讓②去點2呢?答案可以發現是肯定的。

形式化地講,對於數 \(k\) 去點 \(u\) 這個事件,假設這條路徑是 \(v_0=p_k,v_1,v_2,\cdots,v_m=u\),則充要條件分為三部分:

接下來我們著手實現貪心演算法,我們重新確定一下:

我們考慮貪心,從小到大列舉 \(n\) 個數,假設列舉到第 \(k\) 個數(此時已經確定 \(p_\)),找到編號最小的節點 \(u\),滿足 \(k\) 去 \(u\) 的充要條件(也就是一堆時間的大小關係)不與 \(1\sim k-1\) 的條件的並起衝突(指存在一種構造讓這些大小關係都成立)。然後讓 \(k\) 去 \(u\)。

我們對於每乙個點 \(u\) 的鄰接表考慮建乙個圖的模型,將每個邊變成點,「\(x\) 被刪後馬上跟著 \(y\) 被刪」 抽象成 \(x\rightarrow y\) 連一條邊。對於某條邊是鄰接表中最先刪的我們把它叫做 \(\text\),最後刪的叫 \(\text\),我們嘗試挖掘 \(p\) 全部確定後這個圖模型長什麼樣。首先必定存在 \(\text,\text\),其次由於每條邊都要被刪,所以這個圖模型正好有 \(\text_u-1\) 條邊。所以這個圖一定是 \(\text\) 到 \(\text\) 的一條鏈。這也是「存在乙個構造讓鄰接表中的所有大小關係都滿足」的充要條件。

接下來我們考慮在這個鄰接表中只確定了部分大小關係,怎麼判斷是否存在一種構造滿足它們。這等價於我們在圖模型上可以加邊使得最後圖是 \(\text\) 到 \(\text\) 的一條鏈。所以對於這個暫時殘缺的圖,充要條件是:

所以若想要加入一條邊 \((x,y)\) (還未加)仍然合法,充要條件是

若欽定乙個點 \(x\) 是 \(\text\),充要條件是

若欽定乙個點 \(x\) 是 \(\text\),跟上述條件差不多。

接下來考慮複雜度,我們使用並查集來判斷充要條件,列舉 \(k\) 是 \(o(n)\),列舉 \(u\) 是 \(o(n)\),走這一整條路徑是 \(o(n)\),總共 \(o(n^3)\),但考慮到樹上的一些單調性,我們從 \(u\) 開始向外深搜就能 \(o(n)\) 一次判斷每個點的合法性,所以總複雜度 \(o(n^2)\)。

#include using namespace std;

typedef long long ll;

const int maxn = 100005;

int f[maxn << 1], siz[maxn], st[maxn], ed[maxn], in[maxn], out[maxn];

int get(int x)

void merge(int x, int y)

int t, n, p[maxn], ans[maxn], deg[maxn];

struct node e[maxn << 1]; int head[maxn], elen = 1;

void add(int u, int v)

bool ok[maxn], used[maxn]; int fa[maxn], come[maxn];

void dfs(int u, int ff, int com) else

dfs(e[i].v, u, i ^ 1);

}} }

}int main()

st[p[i]] = las;

break;

}} for (int i = 1; i <= n; ++i) printf("%d ", ans[i]); putchar('\n');

for (int i = 1; i <= n; ++i) head[i] = st[i] = ed[i] = deg[i] = used[i] = ans[i] = 0;

for (int i = 1; i <= elen; ++i) in[i] = out[i] = 0;

elen = 1;

} return 0;

}

(CSP2019模擬)閱讀

有n nn個字串,每次可以把每個字串都標記至多乙個字首,但這些字首之間不能有包含關係,求把所有字串的所有字首都標記完的最小次數。資料範圍 n nn,字串總長 100000 le100000 10000 0 如果乙個字串s ss的字首a aa包含於另乙個字串t tt的字首b bb,那麼a aa是t t...

CSP2019初賽遊記

沒想到居然ak了,就紀念一下吧。這次的聯賽,只能說是高三生活的一縷雜音了吧。這次,我的心態可以說是非常平穩了。畢竟不再會有競賽生涯的壓力,也不會有患得患失的惶恐。不過也許我開考前的放聲唱歌驚擾了一些人,對不起了。笑 許多曾同在機房,現在同在教室的同學們,做完之後就趴下了。雖然我覺得確實挺簡單的,但是...

CSP2019退役遊記

本文同步至github 試題答案 謹以此紀念我的oi生涯。晚上xy發表重要講話,保送名額乙個都沒有。預示要涼 早晨6 00起床,本以為在家會睡得好一點確實好一點,在學校會有一堆延遲熄燈的人。然而還是頭暈。準時到校,坐校車,我們被分到和西溪高三一起坐,我左前方是zzy和zrf,只見他們在打雀魂。果然是...