CSP S 2019 洛谷P5658 括號樹

2022-06-14 04:00:10 字數 3398 閱讀 9679

本題中合法括號串的定義如下:

()是合法括號串。

如果a是合法括號串,則(a)是合法括號串。

如果ab是合法括號串,則ab是合法括號串。

本題中子串不同的子串的定義如下:

4. 字串s的子串是s連續的任意個字元組成的字串。s的子串可用起始位置 \(l\) 與終止位置 \(r\) 來表示,記為 \(s (l, r)\)(\(1 \leq l \leq r \leq |s |\),\(|s |\) 表示 s 的長度)。

5.s的兩個子串視作不同當且僅當它們在s中的位置不同,即 \(l\) 不同或 \(r\) 不同。

乙個大小為 \(n\) 的樹包含 \(n\) 個結點和 \(n − 1\) 條邊,每條邊連線兩個結點,且任意兩個結點間有且僅有一條簡單路徑互相可達。

小 q 是乙個充滿好奇心的小朋友,有一天他在上學的路上碰見了乙個大小為 \(n\) 的樹,樹上結點從 \(1\) ∼ \(n\) 編號,\(1\) 號結點為樹的根。除 \(1\) 號結點外,每個結點有乙個父親結點,\(u\)(\(2 \leq u \leq n\))號結點的父親為 \(f_u\)(\(1 ≤ f_u < u\))號結點。

小 q 發現這個樹的每個結點上恰有乙個括號,可能是()。小 q 定義 \(s_i\) 為:將根結點到 \(i\) 號結點的簡單路徑上的括號,按結點經過順序依次排列組成的字串。

顯然 \(s_i\) 是個括號串,但不一定是合法括號串,因此現在小 q 想對所有的 \(i\)(\(1\leq i\leq n\))求出,\(s_i\) 中有多少個互不相同的子串合法括號串

這個問題難倒了小 q,他只好向你求助。設 \(s_i\) 共有 \(k_i\) 個不同子串是合法括號串, 你只需要告訴小 q 所有 \(i \times k_i\) 的異或和,即:

\[(1 \times k_1)\ \text\ (2 \times k_2)\ \text\ (3 \times k_3)\ \text\ \cdots\ \text\ (n \times k_n)

\]其中 \(xor\) 是位異或運算。

我們設\(ans[x]\)表示路徑\((1,x)\)中構成的括號串,以\(x\)節點為右端點的所有區間有多少個合法括號串。

那麼我們就可以在訪問每乙個節點時,依次列舉它的每乙個祖先,如果滿足\(cnt[x][1]-cnt[y][1]=cnt[x][2]-cnt[y][2]\),那麼\(ans[x]++\)。直到\(cnt[y][2]< cnt[y][1]\)時停止列舉。

這樣我們就得到了乙個\(o(n^2)\)的演算法,獲得了\(50pts\)的好成績。

我們發現,其實我們只關心在路徑\((1,x)\)中,深度最大的不滿足\(cnt[p][2]\geq cnt[p][1]\)的節點\(p\)是哪乙個。這樣所有在路徑\((son[p],x)\)中滿足條件\((1)\)的點都可以做貢獻。

其實\((1)\)的條件可以轉化為\(cnt[x][1]-cnt[x][2]=cnt[y][1]-cnt[y][2]\)。所以我們可以用\(pos[s][tot]\)記錄\(cnt[y][1]-cnt[y][2]=s\)的每乙個\(x\)的祖先\(y\)編號。這樣如果\(cnt[x][1]-cnt[x][2]=s\),那麼能對\(x\)做貢獻的點就都在\(pos[s]\)中。

那麼我們可以用乙個棧來記錄\(cnt[p][2]的所有\(p\)。其中\(p\)是\(x\)的祖先。此時如果節點\(x\)為(,那麼直接將\(x\)扔進棧裡。如果\(x\)為),那麼就彈出棧頂。

這樣如果棧頂是\(p\),那麼能對\(x\)做貢獻的就是同時在路徑\((son[p],x)\)和\(pos[cnt[x][1]-cnt[x][2]\)的節點。

所以就可以二分出\(ans[x]\)。

發現\(pos\)中最多隻會有\(n\)個元素,所以可以開乙個\(vector\)。

求出\(ans[x]\)後,路徑\((1,x)\)的合法括號串個數就是\(\sum^x\texttt}_ans[y]\)。做字首和即可。

注意回溯時需要在棧中彈出\(x\)。

時間複雜度\(o(n\log n)\)

#include #include #include #include #include using namespace std;

typedef long long ll;

const int n=500010,inf=1e9;

int n,tot,a[n],cnt[n][3],head[n];

ll ans[n],orz;

char ch;

vectorpos[n*2];

stackdel;

struct edge

e[n];

void add(int from,int to)

int binary(int x,int tp)

return res;

}void dfs(int x,int fa)

int tp=del.top();

pos[s].push_back(inf);

ans[x]=pos[s].size()-binary(s,tp)-1;

pos[s].pop_back();

} ans[x]+=ans[fa];

orz^=1ll*x*ans[x];

pos[s].push_back(x);

for (int i=head[x];~i;i=e[i].next)

dfs(e[i].to,x);

pos[s].pop_back();

if (del.top()==x) del.pop();

if (pp!=-1) del.push(pp);

}int main()

if (ch=='(') a[i]=1;

else a[i]=2;

} for (int i=2,x;i<=n;i++)

del.push(-1);pos[n].push_back(0);

dfs(1,0);

printf("%lld\n",orz);

// for (int i=1;i<=n;i++)

// printf("%lld ",ans[i]);

return 0;

}

洛谷 P5658 括號樹(DFS)

通過資料範圍可以發現,這個題的複雜度要控制在o n o nlogn 之間。所以對於每一次處理,需要o logn 甚至o 1 對於o 1 的處理,可以直接想一下找規律 如果乙個右括號能匹配左括號,且左括號的前乙個括號是乙個已經匹配了的右括號,那麼就可以將這兩個序列合併,當前右括號的貢獻等於前乙個右括號...

洛谷 P5657 CSP S2019 格雷碼

p5657 分析 簽到題,不過也有不少細節。資料範圍需要開unsigned long long,前年也有很多人因此丟了5分。pow會出現神必錯誤,需要手寫乙個mpow函式。演算法 我是記錄當前的 l,r 判斷 k 與 mid 的大小,然後分類討論倒序和正序時選左邊和選右邊手玩的結論。變數 f 代表順...

洛谷P5443 APIO2019 橋梁

apio場外選手沒事休閒做題。看了yyb的題解才把這題做出來 對操作進行分塊,把每 text 個操作分成1組,裡面大概有2類 沒被修改過的和被修改過的。接著對塊內詢問進行離線。對於沒有修改過的 我們可以直接排序然後把他們加入並查集。對於修改過的,我們看他的時間是否大於當前操作的時間,如果大於則加入原...