本題中合法括號串的定義如下:
()
是合法括號串。
如果a
是合法括號串,則(a)
是合法括號串。
如果a
,b
是合法括號串,則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類 沒被修改過的和被修改過的。接著對塊內詢問進行離線。對於沒有修改過的 我們可以直接排序然後把他們加入並查集。對於修改過的,我們看他的時間是否大於當前操作的時間,如果大於則加入原...