数据结构之trie树——First! G,电子字典,Type Printer,Nikitosh and xor
文章目錄
- [USACO12DEC]First! G
- [JSOI2009]電子字典
- [IOI2008] Type Printer
- Nikitosh and xor
[USACO12DEC]First! G
luogu3065
考慮每一個字符串成為答案的可能
這意味著從字典樹根到字符串最后一位就恰好對應重新定義的字典序
在第iii層的時候,想要走特定點,意味著這個點的字典序必須大于該層隸屬于同一個父親的所有點(兄弟)
建立u→vu\rightarrow vu→v的有向邊,表示uuu的字典序<v<v<v
最后肯定字典序不能出現環
拓撲判環
#include <queue> #include <cstdio> #include <cstring> #include <iostream> using namespace std; #define maxn 300005 queue < int > q; int n, cnt, tot; string s[maxn]; bool tag[maxn], vis[maxn], in[26]; int d[26]; bool E[26][26]; int trie[maxn][26];void insert( string s ) {int now = 0;int len = s.length();for( int i = 0;i < len;i ++ ) {int son = s[i] - 'a';if( ! trie[now][son] ) trie[now][son] = ++ cnt;now = trie[now][son];}tag[now] = 1; }bool check( string s ) {memset( d, 0, sizeof( d ) );memset( E, 0, sizeof( E ) );memset( in, 0, sizeof( in ) );int len = s.length();int now = 0;for( int i = 0;i < len;i ++ ) {if( tag[now] ) return 0;int son = s[i] - 'a';for( int j = 0;j < 26;j ++ )if( son ^ j and ! E[son][j] and trie[now][j] ) {E[son][j] = 1;d[j] ++;}now = trie[now][son];}for( int i = 0;i < 26;i ++ )if( ! d[i] ) q.push( i );while( ! q.empty() ) {int now = q.front(); q.pop();in[now] = 1;for( int i = 0;i < 26;i ++ )if( E[now][i] ) {d[i] --;if( ! d[i] ) q.push( i );}}for( int i = 0;i < 26;i ++ )if( ! in[i] ) return 0;return 1; }int main() {scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {cin >> s[i];insert( s[i] );}int ans = 0;for( int i = 1;i <= n;i ++ )if( check( s[i] ) ) vis[i] = 1, ans ++;printf( "%d\n", ans );for( int i = 1;i <= n;i ++ )if( vis[i] ) cout << s[i] << endl;return 0; }[JSOI2009]電子字典
luogu4407
由于一個字符串長度不超過202020,且要求相異必須為111
其實狀態數是可以直接爆搜的
#include <map> #include <cstdio> #include <cstring> using namespace std; #define maxs 25 #define maxn 200005 map < int, int > mp; int n, m, ans, cnt, len; char s[maxn]; bool tag[maxn]; int trie[maxn][26];void insert() {int now = 0;len = strlen( s + 1 );for( int i = 1;i <= len;i ++ ) {int c = s[i] - 'a';if( ! trie[now][c] ) trie[now][c] = ++ cnt;now = trie[now][c];}tag[now] = 1; }bool find() {int now = 0;len = strlen( s + 1 );for( int i = 1;i <= len;i ++ ) {int c = s[i] - 'a';if( ! trie[now][c] ) return 0;now = trie[now][c];}return tag[now]; }void dfs( int ip, int now, bool k ) {if( ip > len ) {if( k ) {ans += ( tag[now] and ! mp[now] );mp[now] = 1;}else for( int i = 0;i < 26;i ++ )if( ! mp[trie[now][i]] and tag[trie[now][i]] )ans ++, mp[trie[now][i]] = 1;return;}int c = s[ip] - 'a';if( k ) {if( ! trie[now][c] ) return;else dfs( ip + 1, trie[now][c], 1 );}else {if( trie[now][c] ) dfs( ip + 1, trie[now][c], 0 );dfs( ip + 1, now, 1 );for( int i = 0;i < 26;i ++ )if( trie[now][i] ) {dfs( ip, trie[now][i], 1 );dfs( ip + 1, trie[now][i], 1 );}} }int main() {scanf( "%d %d", &n, &m );for( int i = 1;i <= n;i ++ ) {scanf( "%s", s + 1 );insert();}for( int i = 1;i <= m;i ++ ) {scanf( "%s", s + 1 );if( find() ) printf( "-1\n" );else {dfs( 1, 0, 0 );printf( "%d\n", ans );ans = 0;mp.clear();}}return 0; }[IOI2008] Type Printer
luogu4683
建立字典樹
貪心的最后剩的字符越多越好
如果這個串不是最后一個,那么這個串肯定會原路返回直到遇到分叉點,走兄弟點
所以求出每個串與最近分叉點的距離,選最大值
然后把這個串一路上的點打上最后訪問標記
接著開始遍歷,先訪問未標記點,再訪問標記點,都清空打印機
最后把直到最后一個打印P為止的所有-操作都扔出操作順序集合
#include <cstdio> #include <vector> #include <cstring> #include <iostream> using namespace std; #define maxn 500005 vector < char > ans; vector < pair < int, int > > G; int n, cnt; char s[25]; bool tag[maxn], vis[maxn]; int dep[maxn], son[maxn]; int trie[maxn][26];void insert() {int len = strlen( s + 1 ), now = 0;for( int i = 1;i <= len;i ++ ) {int c = s[i] - 'a';if( ! trie[now][c] ) trie[now][c] = ++ cnt;dep[trie[now][c]] = dep[now] + 1;now = trie[now][c];}tag[now] = 1; }void dfs1( int now, int lst ) {if( tag[now] ) G.push_back( make_pair( now, max( lst, 0 ) ) );int tot = 0;for( int i = 0;i < 26;i ++ )if( trie[now][i] ) tot ++;for( int i = 0;i < 26;i ++ )if( trie[now][i] )dfs1( trie[now][i], lst == -1 ? ( tot > 1 ? now : lst ) : lst ); }void dfs2( int now, int End, bool &flag ) {if( now == End ) { flag = 1; return; }for( int i = 0;i < 26;i ++ )if( trie[now][i] ) {vis[trie[now][i]] = 1;dfs2( trie[now][i], End, flag );if( flag ) return;vis[trie[now][i]] = 0;} }void dfs3( int now ) {if( tag[now] ) ans.push_back( 'P' );for( int i = 0;i < 26;i ++ ) {if( trie[now][i] and ! vis[trie[now][i]] ) {ans.push_back( i + 'a' );dfs3( trie[now][i] );ans.push_back( '-' );}}for( int i = 0;i < 26;i ++ )if( vis[trie[now][i]] ) {ans.push_back( i + 'a' );dfs3( trie[now][i] );ans.push_back( '-' );} }int main() {memset( son, -1, sizeof( son ) );scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%s", s + 1 );insert();}dfs1( 0, -1 );int Dep = 0, pos;for( int i = 0;i < G.size();i ++ ) {if( dep[G[i].first] - dep[G[i].second] > Dep ) {Dep = dep[G[i].first] - dep[G[i].second];pos = G[i].first;}}bool flag = 0;dfs2( 0, pos, flag );dfs3( 0 );for( int i = ans.size() - 1;~ i;i -- )if( ans[i] != 'P' ) continue;else { cnt = i + 1; break; }printf( "%d\n", cnt );for( int i = 0;i < cnt;i ++ )printf( "%c\n", ans[i] );return 0; }Nikitosh and xor
CodeChef
?i=lrai→br?bl?1\bigoplus_{i=l}^r a_i\rightarrow b_r\bigoplus b_{l-1}?i=lr?ai?→br??bl?1?
枚舉iii,利用字典樹,邊插入邊詢問,將bib_ibi?的二進制位從高到低放入字典樹
求出bib_ibi?與前面某個bjb_jbj?的異或最大值
同理,從后往前操作一樣
?i=lrai→cl?cr+1\bigoplus_{i=l}^r a_i\rightarrow c_l\bigoplus c_{r+1}?i=lr?ai?→cl??cr+1?
枚舉iii,利用字典樹,邊插入邊詢問,將bib_ibi?的二進制位從高到低放入字典樹
求出bib_ibi?與后面某個bjb_jbj?的異或最大值
然后前綴/后綴max遞推
枚舉斷點iii,直接利用prei+sufi+1pre_i+suf_{i+1}prei?+sufi+1?更新答案
#include <cstdio> #include <cstring> #include <iostream> using namespace std; #define int long long #define maxn 400005 int trie[maxn * 30][2]; int a[maxn], b[maxn], c[maxn], pre[maxn], suf[maxn]; int n, cnt;void insert( int x ) {int now = 0;for( int i = 29;~ i;i -- ) {int k = x >> i & 1;if( ! trie[now][k] ) trie[now][k] = ++ cnt;now = trie[now][k];} }int query( int x ) {int now = 0, ans = 0;for( int i = 29;~ i;i -- ) {int k = x >> i & 1;if( trie[now][k ^ 1] )ans += 1 << i, now = trie[now][k ^ 1];elsenow = trie[now][k];}return ans; }signed main() {scanf( "%lld", &n );for( int i = 1;i <= n;i ++ )scanf( "%lld", &a[i] );for( int i = 1;i <= n;i ++ )b[i] = a[i] ^ b[i - 1];for( int i = n;i;i -- )c[i] = c[i + 1] ^ a[i];insert( 0 );for( int i = 1;i <= n;i ++ ) {pre[i] = max( pre[i - 1], query( b[i] ) );insert( b[i] );}memset( trie, 0, sizeof( trie ) );cnt = 0;insert( 0 );for( int i = n;i;i -- ) {suf[i] = max( suf[i + 1], query( c[i] ) );insert( c[i] );}int ans = 0;for( int i = 1;i < n;i ++ )ans = max( ans, pre[i] + suf[i + 1] );printf( "%lld\n", ans );return 0; }總結
以上是生活随笔為你收集整理的数据结构之trie树——First! G,电子字典,Type Printer,Nikitosh and xor的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 线段树/扫描线问卷调查反馈——Rmq P
- 下一篇: 笛卡尔树详解带建树模板及例题运用(Lar