test20180921 量子糾纏
題意
問題描述
萬能的紅太陽J 君正在研究量子信息的糾纏。
具體來說,J 君有一個初始為空的信息集。她會進行m 次操作,有時,她會向信息集內加入一個長度不超過L 的的數字串(一個數字串為一個僅由0 到9 組成的非空字符串),有時她會給出一個數字串,詢問這個數字串是否包含在她的信息集中,有時她會選取兩個長度不超過L 的數字串,使她們在信息集內互相糾纏。
兩個數字串A 和B 互相糾纏代表,對於信息集中的任意串A+C,需滿足B +C 也在信息集中,對於信息集中的任意串B + D,需滿足A + D 也在信息集中(其中+ 代表字符串順序連接,C;D 可能是空串),因此一次糾纏操作可能導致若幹個數字串(可能為無窮多個) 被加入信息集中。
由於兩個數字串的糾纏是一種狀態,在糾纏後的任何時刻都需滿足以上性質,因此第一次糾纏操作之後的任何添加操作都可能導致大於1 個(可能為無窮多個) 數字串被加入信息集。
你需要做的就是回答J 君的所有詢問。
輸入格式
第一行包含一個正整數m,代表操作數。
接下來m 行,每行可能有以下形式:
1 s 代表將數字串s 加入信息集中
2 s 代表詢問數字串s 是否在信息集中
3 a b 代表使數字串a 和b 互相糾纏
輸出格式
對於每一個2 操作,如果詢問串不在集合中,請輸出一行一個整數0,否則輸出一行一個整數1。
樣例輸入
11
1 123
2 123
2 0
3 12 13
1 124
2 133
2 134
2 13
3 1 11
2 111
2 11111111111111111111111124
樣例輸出
1
0
1
1
0
0
1
數據規模與約定
100% 的數據滿足\(m \leq 10^5, c \leq 2050, S \leq 8 \times 10^6,L \leq 50, P \equiv m \mod 10\)
其中c 代表1 操作與3 操作次數之和,S 代表詢問字符串的總長度。
分析
首先考慮沒有3 操作的情況,我們很容易想到使用一個字典樹維護這個集合。
接下來考慮3 操作,對於每個3 操作,我們只需要遞歸合並上述字典樹上的兩個節點即可。
時間復雜度\(O(c^2L + S)\)
這時間復雜度我也不知道是怎麽回事。
代碼
#define yuki(x, y) for(int i = x, __yuki__ = y; i < __yuki__; ++i) #define yukj(x, y) for(int j = x, __yukj__ = y; j < __yukj__; ++j) #define yukii(x, y) for(int i = x, __yukii__ = y; i <= __yukii__; ++i) #define yukji(x, y) for(int j = x, __yukji__ = y; j <= __yukji__; ++j) #define yuk(x, y, z) for(int x = y, __yui__ = z; x < __yui__; ++x) #define yui(x, y, z) for(int x = y, __yuk__ = z; x >= __yuk__; --x) #define sclr(x) memset(x, 0, sizeof(x)) #define sclr1(x) memset(x, -1, sizeof(x)) #define scl(x, y) memset(x, y, sizeof(x)) #define ft first #define sc second #include <cstdio> #include <cstring> #include <algorithm> #include <cassert> using namespace std; typedef long long lol; const int maxp = 1000100; int n, m, alc, refl[1000100], fre[1000100], trans[1000100][10], root; char buf[8000100]; bool mk[1000100]; int gf(int x) { return refl[x] == x ? x : (refl[x] = gf(refl[x])); } void init() { yuki(1, maxp) refl[i] = i; root = ++alc; } void insert(char *ch) { int cur = root; while(*ch) { int x = *ch++-‘0‘; if(!trans[cur][x]) trans[cur][x] = ++alc; cur = gf(trans[cur][x]); } mk[cur] = true; } bool ask(char *ch) { int cur = root; while(*ch) { int x = *ch++-‘0‘; if(!trans[cur][x]) return false; cur = gf(trans[cur][x]); } return mk[cur]; } int &getnode(char *s) { int cur = root; int l = strlen(s); yuki(0, l-1) { int x = s[i]-‘0‘; if(!trans[cur][x]) trans[cur][x] = ++alc; cur = gf(trans[cur][x]); } return trans[cur][s[l-1]-‘0‘]; } void merge(int &a, int &b, bool flag) // 類似線段樹啟發式合並 { a = gf(a); int y = gf(b); // 不用重新定義y if(!a && !y) { if(flag) a = b = ++alc; // 都沒有要補上,為了以後的修改 return; } else if(a == y) // 已經相同就不用補了 return; if(!a) // 單空返回 { a = y; return; } else if(!b) { b = a; return; } refl[y] = a; // 合並子樹的標記 if(mk[y]) mk[a] = true; // 合並單詞的存在性 yuki(0, 10) { merge(trans[a][i], trans[y][i], false); a = gf(a); // 後代可能會對祖先取並 } } void cond() { int &a = getnode(buf); scanf("%s", buf); int &b = getnode(buf); merge(a, b, true); } int main(int argc, char **argv) { freopen("quantum.in", "r", stdin); freopen("quantum.out", "w", stdout); init(); scanf("%d", &n); int t; while(n--) { scanf("%d%s", &t, buf); if(t == 1) insert(buf); else if(t == 2) puts(ask(buf) ? "1" : "0"); else cond(); } return 0; }
test20180921 量子糾纏