CSP-S2019 D1T1 格雷碼
題目
題目描述
通常,人們習慣將所有 位二進制串按照字典序排列,例如所有 位二進制串按字典序從小到大排列爲:,,,。
格雷碼(Gray Code)是一種特殊的 位二進制串排列法,它要求相鄰的兩個二進制串間恰好有一位不同,特別地,第一個串與最後一個串也算作相鄰。
所有 2 位二進制串按格雷碼排列的一個例子爲:,,,。
位格雷碼不止一種,下面給出其中一種格雷碼的生成算法:
- 1 位格雷碼由兩個 位二進制串組成,順序爲:,。
- 位格雷碼的前 個二進制串,可以由依此算法生成的 位格雷碼(總共 個 位二進制串)按順序排列,再在每個串前加一個前綴 構成。
- 位格雷碼的後 個二進制串,可以由依此算法生成的 位格雷碼(總共 個 位二進制串)按逆序排列,再在每個串前加一個前綴 構成。
綜上, 位格雷碼,由 nnn 位格雷碼的 個二進制串按順序排列再加前綴 ,和按逆序排列再加前綴 構成,共 個二進制串。另外,對於 位格雷碼中的 個 二進制串,我們按上述算法得到的排列順序將它們從 編號。
按該算法, 位格雷碼可以這樣推出:
- 已知 1 位格雷碼爲 ,。
- 前兩個格雷碼爲 ,。後兩個格雷碼爲 ,。合併得到 ,,,,編號依次爲 。
同理, 位格雷碼可以這樣推出:
- 已知 位格雷碼爲:,,,。
- 前四個格雷碼爲:,,,。後四個格雷碼爲:,,,。合併得到:,,,,,,,,編號依次爲 。
現在給出 ,,請你求出按上述算法生成的 位格雷碼中的 號二進制串。
分析
簡單列出 的情況就可以發現,對於第 個串,去掉它的最後一位就和 的第 位上的串一樣了,且不難發現最後一位成 , , , 循環,於是直接遞歸處理。
注意題目數據要求開 unsigned long long
。
參考代碼
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ull;
int N;
ull K;
string s;
void DFS(int t, ull k) {
if(t == 0) return;
DFS(t - 1, k / 2);
if(k % 4 == 2 || k % 4 == 1) s += '1';
else s += '0';
}
int main() {
// freopen("code.in", "r", stdin);
// freopen("code.out", "w", stdout);
cin >> N >> K;
DFS(N, K);
cout << s << endl;
return 0;
}
D1T2 括號樹
題目
題目描述
小 Q 是一個充滿好奇心的小朋友,有一天他在上學的路上碰見了一個大小爲 的樹,樹上結點從 編號, 號結點爲樹的根。除 號結點外,每個結點有一個父親結點,()號結點的父親爲 ()號結點。
小 Q 發現這個樹的每個結點上恰有一個括號,可能是( 或)。小 Q 定義 爲:將根結點到 號結點的簡單路徑上的括號,按結點經過順序依次排列組成的字符串。
顯然 是個括號串,但不一定是合法括號串,因此現在小 Q 想對所有的 ()求出, 中有多少個互不相同的子串是合法括號串。
這個問題難倒了小 Q,他只好向你求助。設 共有 個不同子串是合法括號串, 你只需要告訴小 Q 所有 的異或和,即:
其中 是位異或運算。
分析
就不講我考場上怎麼做的吧,要看的話直接去我的爆炸記裏面看就可以了。。。
令 爲從節點 到節點 的路徑上的括號序列中,加入 的括號後新增加的合法匹配的括號序列的數量。
則節點 的答案爲從根到 的路徑上的 之和。
考慮怎麼轉移這個東西。
我們用一個棧來記錄左括號的位置。
則對於一個節點,我們分兩種情況來討論:
- 若當前節點是左括號:將這個節點塞進棧裏,顯然沒有新增合法括號匹配,令 即可;
- 若當前節點是右括號,我們再分兩種情況討論:
- 若當前棧是空的,則這個節點上也沒有新增合法括號匹配,令 即可;
- 否則取出棧頂 ,不難發現節點 可以和 匹配增加一個合法的括號匹配,此時我們應令 。
注意在向下遞歸完後要恢復現場。 不要學我開 個棧。。。
參考代碼
#include <stack>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
const int Maxn = 5e5;
int N;
char s[Maxn + 5];
vector<int> G[Maxn + 5];
void addedge(int u, int v) {
G[u].push_back(v), G[v].push_back(u);
}
stack<int> stk;
ll f[Maxn + 5], g[Maxn + 5];
int fa[Maxn + 5];
void DFS(int u, int pre) {
int tp = -1;
if(s[u] == '(') stk.push(u);
else {
if(!stk.empty()) {
tp = stk.top();
g[u] = g[fa[tp]] + 1;
stk.pop();
}
}
f[u] = f[fa[u]] + g[u];
for(int i = 0; i < (int)G[u].size(); i++) {
int v = G[u][i];
if(v == pre) continue;
DFS(v, u);
}
if(tp != -1) stk.push(tp);
else if(!stk.empty()) stk.pop();
}
int main() {
#ifdef LOACL
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
scanf("%d", &N);
scanf("%s", s + 1);
for(int i = 2; i <= N; i++) {
scanf("%d", &fa[i]);
addedge(fa[i], i);
}
DFS(1, 0);
ll ans = 0;
for(int i = 1; i <= N; i++)
ans ^= (1LL * i * f[i]);
printf("%lld\n", ans);
return 0;
}