提高组字符串专题1
A [NOIP2020] 字符串匹配
枚举循环节 ,找到最多的循环节,剩下的一定包含在 。然后可以发现的是 ,那么就只用考虑 和 的贡献即可。复杂度 。
#include<bits/stdc++.h> using namespace std; constexpr int N = (1 << 20) + 5; uint64_t h[N], p[N], cnt[26], pnum[N]; inline uint64_t geth(int l, int r){ return h[r] - h[l-1] * p[r-l+1]; } namespace BIT{ #define lb(x) ((x) & (-x)) int t[30]; inline void add(int pos, int val){ for(; pos<=29; pos+=lb(pos)) t[pos] += val; } inline int query(int pos){ int res = 0; for(; pos; pos-=lb(pos)) res += t[pos]; return res; } inline void clear(){ memset(t, 0, sizeof(t)); } } int main(){ p[0] = 1; for(int i=1; i<=1<<20; ++i) p[i] = p[i-1] * 131ull; int T; cin>>T; while(T--){ string str; cin>>str; int n = str.size(); for(int i=1; i<=n; ++i) h[i] = h[i-1] * 131ull + str[i-1]; for(int i=n; i>=1; --i) pnum[i] = (++cnt[str[i-1] - 'a'] & 1) ? pnum[i+1]+1 : pnum[i+1]-1; memset(cnt, 0, sizeof(cnt)); long long ans = 0; for(int i=2, num=0; i<n; ++i){ int pos = i; while(pos+i < n && geth(pos+1, pos+i) == geth(1, i)) pos += i; (++cnt[str[i-2] - 'a'] & 1) ? ++num : --num; BIT::add(num+1, 1); int nm = pos / i - 1, res1 = BIT::query(pnum[pos+1]+1), res2 = BIT::query(pnum[pos-i+1]+1); ans += (nm / 2 + 1) * res1 + (nm - nm / 2) * res2; } cout<<ans<<'\n'; BIT::clear(); memset(cnt, 0, sizeof(cnt)); memset(pnum, 0, sizeof(pnum)); } }
B Short Code
建 Trie,dfs遍历,每次把深度最深的节点提到当前节点。用优先队列维护即可。
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e5 + 5; long long ans; namespace Trie{ int t[N][26], cnt; bitset<N> flag; priority_queue<int> q[N]; inline void insert(string s){ int len = s.size(), id = 0; for(int i=0; i<len; ++i){ int ch = s[i] - 'a'; if(!t[id][ch]) t[id][ch] = ++cnt; id = t[id][ch]; } flag[id] = 1; q[id].emplace(len); ans += len; } inline void solve(int u, int dep){ for(int i=0; i<26; ++i) if(t[u][i]){ int v = t[u][i]; solve(v, dep + 1); while(!q[v].empty()){ q[u].emplace(q[v].top()); q[v].pop(); } } if(u && !flag[u]){ ans -= q[u].top() - dep; q[u].pop(); q[u].emplace(dep); } } } string str[N]; int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin>>n; for(int i=1; i<=n; ++i) cin>>str[i], Trie::insert(str[i]); Trie::solve(0, 0); return cout<<ans, 0; }
C 串串题
考虑用 去匹配 ,匹配出来的一定是一段区间,令 表示一段区间内非 包含的元素种类数, 表示 中元素种类数,贡献即为 。预处理出所有可能匹配的区间即可。
#include<bits/stdc++.h> using namespace std; constexpr int M = 1e9 + 7, N = 1e6 + 5; int a[N], b[N], nxt[N], fac[N], inv[N], val[N], pos[N], l[N], r[N], c[N]; bitset<N> vis; inline int qpow(int a, int k){ int res = 1; while(k){ if(k & 1) res = (long long)res * a % M; a = (long long)a * a % M; k >>= 1; } return res; } inline int add(initializer_list<int> Add){ int res = 0; for(int v : Add) res = res + v >= M ? res + v - M : res + v; return res; } inline int mul(initializer_list<int> Mul){ int res = 1; for(int v : Mul) res = (long long)res * v % M; return res; } inline int C(int a, int b){ return b > a ? 0 : mul({fac[a], inv[b], inv[a-b]}); } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); fac[0] = inv[0] = 1; for(int i=1; i<N; ++i) fac[i] = mul({fac[i-1], i}); inv[N-1] = qpow(fac[N-1], M-2); for(int i=N-2; i>=1; --i) inv[i] = mul({inv[i+1], i+1}); int T; cin>>T; while(T--){ int n, m, w, d, p = 0; cin>>n>>m>>w>>d; for(int i=1; i<=n; ++i) cin>>a[i]; for(int i=1; i<=m; ++i) cin>>b[i], vis[b[i]] = 1; for(int i=1; i<=n; ++i) if(vis[a[i]]) pos[++p] = i, c[p] = a[i]; for(int i=2, j=0; i<=m; ++i){ while(j && (b[i] ^ b[j+1])) j = nxt[j]; nxt[i] = j + (b[i] == b[j+1]); j = nxt[i]; } int ans = 0, q = 0; d += vis.count(); for(int i=1, j=0; i<=p; ++i){ while(j && (c[i] ^ b[j+1])) j = nxt[j]; if(j < m && c[i] == b[j+1]) ++j; if(j == m) l[++q] = pos[i-m+1], r[q] = pos[i], j = nxt[j]; } for(int i=1, j=1, k=1, cnt=0; i<=n && k<=q; ++i){ if(!val[a[i]]++) ++cnt; if(r[k] == i){ while(j < l[k]) if(!--val[a[j++]]) --cnt; ++k; if(d >= cnt) ans = add({ans, C(w-cnt, d-cnt)}); } } cout<<ans<<'\n'; vis.reset(); for(int i=1; i<=n; ++i) val[a[i]] = 0; } return 0; }
D [COCI2016-2017#1] Cezar
考虑建出 Trie 树,然后在每两个字符串不同的位置的字符按照最终排名建边。然后跑拓扑。如果有环则 NE
,否则按照拓扑序输出即可。注意特判前缀的情况。
#include<bits/stdc++.h> using namespace std; string str[105]; int in[30000], rk[10005], ans[30000]; vector<int> G[30000]; queue<int> q; namespace Trie{ #define p pair<int, int> int t[10005][26], cnt, son[10005*26], ed[10005*26]; vector<p> vec[10005*26]; inline void insert(string s, int ai){ int id = 0, len = s.size(); for(int i=0; i<len; ++i){ int ch = s[i] - 'a'; if(!t[id][ch]) t[id][ch] = ++cnt, son[cnt] = 114; vec[id].emplace_back(p{ai, ch}); son[id] = min(rk[ai], son[id]); id = t[id][ch]; } ed[id] = rk[ai]; } inline void solve(int u){ if(ed[u] && ed[u] > son[u]) cout<<"NE", exit(0); if(vec[u].empty()) return; sort(vec[u].begin(), vec[u].end(), [](p x, p y){ return rk[x.first] < rk[y.first]; }); for(int i=0; i<vec[u].size()-1; ++i) if(vec[u][i].second ^ vec[u][i+1].second){ G[vec[u][i].second].emplace_back(vec[u][i+1].second); ++in[vec[u][i+1].second]; } for(int i=0; i<26; ++i) if(t[u][i]) solve(t[u][i]); } } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin>>n; Trie::son[0] = 114; for(int i=1; i<=n; ++i) cin>>str[i]; for(int i=1, r; i<=n; ++i) cin>>r, rk[r] = i; for(int i=1; i<=n; ++i) Trie::insert(str[i], i); Trie::solve(0); for(int i=0; i<26; ++i) if(!in[i]) q.emplace(i); int cnt = 0; while(!q.empty()){ int u = q.front(); q.pop(); ans[u] = ++cnt; for(int v : G[u]){ if(!in[v]) return cout<<"NE", 0; if(!--in[v]) q.emplace(v); } } auto check = [&](){ for(int i=0; i<26; ++i) if(in[i]) return false; return true; }; if(!check()) return cout<<"NE", 0; cout<<"DA\n"; for(int i=0; i<26; ++i) cout<<char(ans[i]-1+'a'); return 0; }
E [CSP-S 2023] 消消乐
用一个 pre
数组维护最小 使得 合法。 直到 为 ,或者 。维护 数组表示以 为右端点的合法区间数量,若 不为 那么有 ,否则为 。
#include<bits/stdc++.h> using namespace std; constexpr int N = 2e6 + 5; int pre[N], val[26]; long long f[N], sum; int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; string s; cin>>n>>s; for(int i=2; i<=n; ++i){ pre[i] = i - 1; while(pre[pre[i]] && (s[i-1] ^ s[pre[i]-1])) pre[i] = pre[pre[i]] - 1; s[i-1] == s[pre[i]-1] ? sum += f[i] = f[pre[i]-1] + 1 : f[i] = pre[i] = 0; } return cout<<sum, 0; }
F [CSP-S 2022] 星战
科技 和哈希。考虑到只要图上所有点的出度都为 ,那么一定合法。维护出度可以做到 。考虑维护入度,维护入度是 的,并且图上的入度和一定等于出度和,考虑到合法情况的出度和为 ,那么合法情况的入度和也一定为 ,但是这 个出度是均匀分布在每一个点上的,也就是这 个入度是由每一个点贡献的,那么给每一个点随一个权值即可。
#include<bits/stdc++.h> using namespace std; #define int long long constexpr int N = 5e5 + 5; int in[N], val[N], f[N]; mt19937 mtrd(time(0)); signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n, m; cin>>n>>m; int sum = 0; for(int i=1; i<=n; ++i) val[i] = mtrd(); int bas = accumulate(val+1, val+1+n, 0ll); for(int i=1, u, v; i<=m; ++i){ cin>>u>>v; f[v] += val[u]; in[v] += val[u], sum += val[u]; } int q; cin>>q; while(q--){ int opt, u, v; cin>>opt; if(opt == 1) cin>>u>>v, in[v] -= val[u], sum -= val[u]; else if(opt == 2) cin>>u, sum -= in[u], in[u] = 0; else if(opt == 3) cin>>u>>v, in[v] += val[u], sum += val[u]; else cin>>u, sum += f[u] - in[u], in[u] = f[u]; cout<<(sum == bas ? "YES\n" : "NO\n"); } return 0; }
F Comfortably Numb
首先将区间异或转化为异或前缀和,然后考虑分治处理区间。对于 ,定义 ,分别规定最大值由左端点和右端点取得,双指针维护即可。在 trie 树上求最大异或即可。
#include<bits/stdc++.h> using namespace std; constexpr int N = 2e5 + 5, T = 1e7 + 5; int n, a[N], b[N], ans; namespace Trie{ int t[T][2], cnt; inline void insert(int x){ int id = 0, b = 0; for(int i=30; i>=0; --i){ b = 1 & (x >> i); if(!t[id][b]) t[id][b] = ++cnt; id = t[id][b]; } } inline int query(int x){ if(!cnt) return 0; int id = 0, b = 0, res = x; for(int i=30; i>=0; --i){ b = (1 & (x >> i)) ^ 1; if(t[id][b]) id = t[id][b], res ^= b << i; else id = t[id][b^1], res ^= (b^1) << i; } return res; } inline void clear(){ for(int i=0; i<=cnt; ++i) t[i][0] = t[i][1] = 0; cnt = 0; } } inline void mao(int l, int r){ if(l == r) return; int mid = (l + r) >> 1; for(int L=mid, R=mid, mx=a[mid]; L>=l; --L){ mx = max(mx, a[L]); while(R < r && a[R+1] <= mx) ++R, Trie::insert(b[R]); ans = max(ans, Trie::query(b[L-1] ^ mx)); } Trie::clear(); for(int R=mid+1, L=mid+1, mx=a[mid+1]; R<=r; ++R){ mx = max(mx, a[R]); while(L > l && a[L-1] <= mx) --L, Trie::insert(b[L-1]); ans = max(ans, Trie::query(b[R] ^ mx)); } Trie::clear(); mao(l, mid), mao(mid+1, r); } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int T; cin>>T; while(T--){ cin>>n; ans = 0; for(int i=1; i<=n; ++i) cin>>a[i]; for(int i=1; i<=n; ++i) b[i] = b[i-1] ^ a[i]; mao(1, n); cout<<ans<<'\n'; } return 0; }
H Hossam and Range Minimum Que
给每个点随一个权值,找到区间异或不为 的点即可。主席树维护。
#include<bits/stdc++.h> using namespace std; #define int unsigned int constexpr int N = 2e5 + 5; int a[N], b[N], rt[N], val[N], pos[N]; mt19937 mtrd(time(0)); namespace ST{ struct node{ int l, r, ls, rs, val; }t[N*30]; int cnt; inline void build(int &id, int l, int r){ id = ++cnt; t[id].l = l, t[id].r = r; if(l == r) return; int mid = (l + r) >> 1; build(t[id].ls, l, mid), build(t[id].rs, mid+1, r); } inline void modify(int &id, int pre, int pos, int val){ t[id = ++cnt] = t[pre]; if(t[id].l == t[id].r) return t[id].val ^= val, void(); int mid = (t[id].l + t[id].r) >> 1; if(pos <= mid) modify(t[id].ls, t[pre].ls, pos, val); else modify(t[id].rs, t[pre].rs, pos, val); t[id].val = t[t[id].ls].val ^ t[t[id].rs].val; } inline int query(int x, int y){ if(!x || !y) return 0; int val = t[x].val ^ t[y].val; if(!val) return 0; if(t[x].l == t[x].r) return t[x].l; if(t[t[x].ls].val ^ t[t[y].ls].val) return query(t[x].ls, t[y].ls); else return query(t[x].rs, t[y].rs); } } signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); int n; cin>>n; for(int i=1; i<=n; ++i) cin>>a[i], b[i] = a[i]; sort(b+1, b+1+n); int len = unique(b+1, b+1+n) - b - 1; for(int i=1; i<=n; ++i) pos[i] = lower_bound(b+1, b+1+len, a[i]) - b; ST::build(rt[0], 1, len); for(int i=1; i<=len; ++i) val[i] = mtrd(); for(int i=1; i<=n; ++i) ST::modify(rt[i], rt[i-1], pos[i], val[pos[i]]); int q, ans = 0; cin>>q; while(q--){ int l, r; cin>>l>>r; l ^= ans, r ^= ans; ans = b[ST::query(rt[l-1], rt[r])]; cout<<ans<<'\n'; } return 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18564886
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步