2021ICPC沈阳区域赛BEFIJM

E. Edward Gaming, the Champion

题意

给定长度不超过2e52\mathrm{e}52e5的文本串,统计其中"edgnb"出现的次数.

代码 -> 2021ICPC沈阳-E(KMP)

const int MAXN = 2e5 + 5;
int n = 5, m;  // 模式串长度、文本串长度
char p[MAXN] = "0edgnb", s[MAXN];  // 模式串、文本串
int nxt[MAXN];  // KMP的next数组void init() {  // 预处理出模式串的next数组for (int i = 2, j = 0; i <= n; i++) {while (j && p[i] != p[j + 1]) j = nxt[j];if (p[i] == p[j + 1]) j++;nxt[i] = j;}
}int main() {init();cin >> s + 1; m = strlen(s + 1);int ans = 0;for (int i = 1, j = 0; i <= m; i++) {while (j && s[i] != p[j + 1]) j = nxt[j];if (s[i] == p[j + 1]) j++;if (j == n) {ans++;j = nxt[j];}}cout << ans;
}


F. Encoded Strings I

题意

给定一个长度为n(1≤n≤1000)n\ \ (1\leq n\leq 1000)n  (1≤n≤1000)、只包含前202020个小写英文字母的字符串sss,将其所有非空前缀按如下规则解码,输出解码后字典序最大的字符串.解码规则:设sss中的字符ccc在sss中最后一次出现的位置之后(不包括该位置)有xxx个不同字符,将ccc替换为第(x+1)(x+1)(x+1)个英文字母.

样例解释

注意到sss的最后一个字符必是该字母最后一次出现的位置,且其后无其他字母,故sss中所有该字母必会被替换为a.

显然sss中的所有相同字母会被替换成同一的字母.

字符串下标从000开始.

[样例I] s=s=s=“abc”

​ ①a字母最后一次出现的位置是s[0]s[0]s[0],其后有b、c两个不同字母,故将其替换为第(2+1)(2+1)(2+1)个字母,即c.

​ ②b字母最后一次出现的位置是s[1]s[1]s[1],其后有c一个不同字母,故将其替换为第(1+1)(1+1)(1+1)个字母,即b.

​ ③c字母是最后一个字母,替换为a.

​ 解码结果:“cba”.

[样例II] s=s=s=“cac”

​ ①第一个c字母最后一次出现的位置是s[2]s[2]s[2],是最后一个字母,替换为a.

​ ②a字母最后一次出现的位置是s[1]s[1]s[1],其后有c一个不同字母,故将其替换为第(1+1)(1+1)(1+1)个字母,即b.

​ ③第二个c字母是最后一个字母,替换为a.

​ 解码结果:“aba”.

[样例III] s=s=s=“aacc”

​ ①第一个a字母最后一次出现的位置是s[1]s[1]s[1],其后有c一个不同字母,故将其替换为第(1+1)(1+1)(1+1)个字母,即b.同理第二个a替换为b.

​ ②第一个c字母最后一次出现的位置是s[3]s[3]s[3],是最后一个字母,故将第一、二个c替换为a.

​ 解码结果:“bbaa”.

思路

将sss反转变为s′s's′,枚举s′s's′的起点,从前往后扫一遍s′s's′,s′s's′的每一个起点对应sss的一个非空前缀.s′s's′中字母chchch第一次出现的位置即chchch在sss中最后一次出现的位置,只需用set统计s′s's′中chchch之前出现了几种字母,将结果存在map<char,char>中,最后将sss的对应前缀的字母替换为map中的字母,更新字典序最大的答案.因sss中的所有相同字母会被替换成同一字母,故每个前缀中每个字母只需更新一次.

以s′[0]s'[0]s′[0]为起点的长度为nnn,以s′[1]s'[1]s′[1]为起点的长度为(n−1),⋯,(n-1),\cdots,(n−1),⋯,以s′[n−1]s'[n-1]s′[n−1]为起点的长度为111,故循环n+(n−1)+⋯+1=n(n+1)2n+(n-1)+\cdots+1=\dfrac{n(n+1)}{2}n+(n−1)+⋯+1=2n(n+1)​次.同理替换sss的前缀的字符需n(n+1)2\dfrac{n(n+1)}{2}2n(n+1)​次,总时间复杂度O(n2)O(n^2)O(n2),nnn最大100010001000,可过.

代码 -> 2021ICPC沈阳-F(模拟)

int n;  // 字符串长度
string s;
string ans = "";
set<char> chcnt;  // 统计字符出现的次数
map<char, char> change;  // 记录字符要替换为什么字符int main() {cin >> n >> s;string ss = s; reverse(ss.begin(), ss.end());  // 将s倒置for (int i = 0; i < n; i++) {  // 枚举ss的起点chcnt.clear();for (int j = i; j < n; j++) {  // 从前往后扫一遍ssif (chcnt.count(ss[j])) continue;  // 更新过int cnt = chcnt.size();  // 之前出现过的不同字母的数量change[ss[j]] = char('a' + cnt);chcnt.insert(ss[j]);  // 注意更新完cnt后再将该字符插入集合}string tmp = s.substr(0, n - i);  // 取出s中对应的前缀for (auto& item : tmp) item = change[item];  // 注意取引用ans = max(ans, tmp);}cout << ans;
}


J. Luggage Lock

题意

有一个四位数密码锁,每次操作可将一段连续的数字同时向上或向下转动一格.有T(1≤T≤1e5)T\ \ (1\leq T\leq 1\mathrm{e}5)T  (1≤T≤1e5)组询问,每组询问输入一行,包含两个密码状态a0a1a2a3a_0a_1a_2a_3a0​a1​a2​a3​和b0b1b2b3b_0b_1b_2b_3b0​b1​b2​b3​.求将前者转化为后者的最少操作次数.

思路

显然BFS,从a0a1a2a3a_0a_1a_2a_3a0​a1​a2​a3​开始搜,时间复杂度O(104)O(10^4)O(104),而TTT最大1e51\mathrm{e}51e5,会T.

注意到从一个状态转移到另一个状态只与两状态的数码的差值有关,则可将所有状态化为从000000000000开始的状态.设a0a1a2a3a_0a_1a_2a_3a0​a1​a2​a3​转化为000000000000至少需xxx次操作,b0b1b2b3b_0b_1b_2b_3b0​b1​b2​b3​转化为000000000000至少需yyy次操作,则最终答案ans=∣x−y∣ans=|x-y|ans=∣x−y∣.

预处理出从000000000000开始能转移到的状态的最小操作次数,共1e41\mathrm{e}41e4种状态.每组测试样例O(1)O(1)O(1)查询,总时间复杂度O(1e4)O(1\mathrm{e}4)O(1e4).

每个状态用字符串表示.BFS的过程:枚举字符串的后缀的起点,得到这段后缀同时的+1+1+1、−1-1−1的状态,若状态未搜过,则入队并记录步数.

代码 -> 2021ICPC沈阳-J(BFS)

map<string, int> state;  // 记录状态及其步数void bfs() {queue<string> que;que.push("0000"), state["0000"] = 0;  // 起点while (que.size()) {auto u = que.front(); que.pop();for (int i = 0; i < 4; i++) {  // 枚举字符串后缀的起点for (int j = i; j < 4; j++) {string up = u, down = u;  // 记录+1、-1的结果for (int k = i; k <= j; k++) {up[k] = '0' + (u[k] - '0' + 1) % 10;  // +1down[k] = '0' + (u[k] - '0' + 9) % 10;  // -1,其中+9即-1+10}if (!state.count(up)) {que.push(up);state[up] = state[u] + 1;  // 更新步数}if (!state.count(down)) {que.push(down);state[down] = state[u] + 1;  // 更新步数}}}}
}int main() {bfs();CaseT{string a,b; cin >> a >> b;for (int i = 0; i < 4; i++) a[i] = '0' + (a[i] - b[i] + 10) % 10;cout << state[a] << endl;}
}


B. Bitwise Exclusive-OR Sequence

题意

现有n(1≤n≤1e5)n\ \ (1\leq n\leq 1\mathrm{e}5)n  (1≤n≤1e5)个非负数a1,⋯,ana_1,\cdots,a_na1​,⋯,an​,用m(0≤m≤2e5)m\ \ (0\leq m\leq 2\mathrm{e}5)m  (0≤m≤2e5)个限制auxorav=wa_u\ xor\ a_v=wau​ xor av​=w描述.若满足限制的序列存在,输出ai(i=1,⋯,n)a_i\ \ (i=1,\cdots,n)ai​  (i=1,⋯,n)之和的最小值;否则输出−1-1−1.

思路I By : yezzz.

若auxorav=wa_u\ xor\ a_v=wau​ xor av​=w,则在节点uuu和vvv间连一条权值为www的无向边.问题转化为求各节点点权之和的最小值.

注意到mmm条限制不能保证所连成的图是树,则可能出现重边和环:①出现重边时,若两重边权值不同,则无解;②出现环时,以三元环为例,设三个节点分别为AAA、BBB、CCC,且ABABAB、BCBCBC、CACACA的边权分别为w1,w2,w3w_1,w_2,w_3w1​,w2​,w3​.若满足限制的序列存在,应满足w1∧w2∧w3=A∧B∧B∧C∧C∧A=0w_1\ ^\wedge w_2\ ^\wedge w_3=A\ ^\wedge B\ ^\wedge B\ ^\wedge C\ ^\wedge C\ ^\wedge A=0w1​ ∧w2​ ∧w3​=A ∧B ∧B ∧C ∧C ∧A=0,即环上的每个节点都会被异或两次.

遇到重边和环即判断是否无解,剩下的都是链.考察链A1A2⋯AnA_1A_2\cdots A_nA1​A2​⋯An​,设A1∧A2=w1,A2∧A3=w2,⋯,An−1∧An=wn−1A_1\ ^\wedge A_2=w_1,A_2\ ^\wedge A_3=w_2,\cdots,A_{n-1}\ ^\wedge A_n=w_{n-1}A1​ ∧A2​=w1​,A2​ ∧A3​=w2​,⋯,An−1​ ∧An​=wn−1​.注意到A2=w1∧A1,A3=w2∧A2=(w1∧w2)∧A1,⋯,An=(w1∧w2∧⋯∧wn−1)∧A1A_2=w_1\ ^\wedge A_1,A_3=w_2\ ^\wedge A_2=(w_1\ ^\wedge w_2)\ ^\wedge A_1,\cdots,A_n=(w_1\ ^\wedge w_2\ ^\wedge \cdots\ ^\wedge w_{n-1})\ ^\wedge A_1A2​=w1​ ∧A1​,A3​=w2​ ∧A2​=(w1​ ∧w2​) ∧A1​,⋯,An​=(w1​ ∧w2​ ∧⋯ ∧wn−1​) ∧A1​,即A2,⋯,AnA_2,\cdots,A_nA2​,⋯,An​由A1A_1A1​唯一确定.而每一位的异或值仅与两数的该位数码是否相同有关,则可枚举A1A_1A1​的二进制表示的数位.遍历链时维护链上的节点的前缀异或和,确定A1A_1A1​后用其快速求出A2,⋯,AnA_2,\cdots,A_nA2​,⋯,An​.

DFS.注意到确定节点uuu的点权后,与其连通的所有节点的点权也唯一确定,则DFS维护a1,⋯,ana_1,\cdots,a_na1​,⋯,an​分别所在的连通块,每次遍历以节点centercentercenter为中心的菊花图.若centercentercenter的某条出边的终点vvv已被遍历过,则出现重边或自环,如下图:


对a1,⋯,ana_1,\cdots,a_na1​,⋯,an​分别所在的连通块,考察它对答案的贡献.设第pospospos个连通块共tottottot个节点.枚举A1A_1A1​的数位,统计tottottot个节点中对应数位为111的个数cntcntcnt.若tottottot个节点中该数位111的数量比000的数量多,为使ai(i=1,⋯,n)a_i\ \ (i=1,\cdots,n)ai​  (i=1,⋯,n)的总和最小,A1A_1A1​的该数位应取111,否则取000.确定A1A_1A1​的值后,用前缀异或和求出A2,⋯,AnA_2,\cdots,A_nA2​,⋯,An​,它们之和即该连通块对答案的贡献.

菊花图的遍历还可用BFS.

代码I -> 2021ICPC沈阳-B(DFS)

const int MAXN = 1e5 + 5;
int n, m;  // 节点数、限制数
vii graph[MAXN];  // graph[u]={v,w}
bool vis[MAXN];  // 记录每个节点是否已被遍历过
ll pre[MAXN];  // 每条链上的前缀异或和
vi to[MAXN];  // 与每个节点连通的点void dfs(int pos, int center) {  // 遍历到第pos个节点所在的连通块,当前菊花图的中心为centervis[center] = true;for (auto item : graph[center]) {  // 枚举中心节点的所有出边int v = item.first, w = item.second;if (vis[v]) {  // 重边或成环if ((pre[center] ^ w) != pre[v]) {  // 检查边权异或值是否矛盾,注意位运算符优先级低cout << -1;exit(0);}}else {pre[v] = pre[center] ^ w;  // 维护前缀异或和to[pos].push_back(v);  // 该点可与第pos个节点连通dfs(pos, v);  // 搜以节点v为中心的菊花图}}
}ll cal(int pos) {  // 计算第pos个节点所在的连通块对答案的贡献int tot = to[pos].size();  // 第pos个节点所在的连通块的节点数int a1 = 0;for (int i = 0; i < 30; i++) {  // 枚举a1的数位int cnt = 0;  // 统计与第pos个节点连通的节点的点权中第i位为1的节点数for (auto v : to[pos]) // 枚举与第pos个节点连通的节点if ((pre[v] >> i) & 1) cnt++;if (cnt > tot - cnt) a1 += (1 << i);  // 若1比0多,则a1的第i位取1可使总和最小}ll res = a1;for (auto v : to[pos]) res += pre[v] ^ a1;return res;
}int main() {cin >> n >> m;while (m--) {int u, v, w; cin >> u >> v >> w;graph[u].push_back(make_pair(v, w)), graph[v].push_back(make_pair(u, w));}ll ans = 0;for (int i = 1; i <= n; i++) {if (!vis[i]) {dfs(i, i);  // 遍历到第i个节点所在的连通块,当前菊花图的中心为第i个节点ans += cal(i);  // 计算第i个节点所在的连通块对答案的贡献}}cout << ans;
}

代码II -> 2021ICPC沈阳-B(BFS)

const int MAXN = 1e5 + 5;
int n, m;  // 节点数、限制数
vii graph[MAXN];  // graph[u]={v,w}
bool vis[MAXN];  // 记录每个节点是否已被遍历过
ll pre[MAXN];  // 每条链上的前缀异或和
vi to[MAXN];  // 与每个节点连通的点void bfs(int pos) {  // 遍历到第pos个连通块qi que;que.push(pos);vis[pos] = true;while (que.size()) {int u = que.front(); que.pop();for (auto item : graph[u]) {  // 枚举u的所有出边int v = item.first, w = item.second;if (vis[v]) {  // 出现重边或自环if ((pre[u] ^ w) != pre[v]) {  // 检查边权异或值是否矛盾cout << -1;exit(0);}}else {vis[v] = true;pre[v] = pre[u] ^ w;to[pos].push_back(v);que.push(v);}}}
}ll cal(int pos) {  // 计算第pos个节点所在的连通块对答案的贡献int tot = to[pos].size();  // 第pos个节点所在的连通块的节点数int a1 = 0;for (int i = 0; i < 30; i++) {  // 枚举a1的数位int cnt = 0;  // 统计与第pos个节点连通的节点的点权中第i位为1的节点数for (auto v : to[pos]) // 枚举与第pos个节点连通的节点if ((pre[v] >> i) & 1) cnt++;if (cnt > tot - cnt) a1 += (1 << i);  // 若1比0多,则a1的第i位取1可使总和最小}ll res = a1;for (auto v : to[pos]) res += pre[v] ^ a1;return res;
}int main() {cin >> n >> m;while (m--) {int u, v, w; cin >> u >> v >> w;graph[u].push_back(make_pair(v, w)), graph[v].push_back(make_pair(u, w));}ll ans = 0;for (int i = 1; i <= n; i++) {if (!vis[i]) {bfs(i);  // 遍历第i个节点所在的连通块ans += cal(i);  // 计算第i个节点所在的联通块对答案的贡献}}cout << ans;
}

DFS:

BFS:


思路II By : AC__dream

对边权的每个数位维护一个连通块,其中边权的二进制表示中对应数位为111时连一条边权为111的边,否则连一条边权为000的边.因w<230w<2^{30}w<230,则图中有303030个连通块,分别对它们染色,每个节点染111或000,则每个连通块中111的个数的最小值乘对应的权值即为该数位对答案的贡献.

111的个数不确定的原因:将一张已染色的图颜色反转后仍满足条件.

代码III -> 2021ICPC沈阳-B(染色)

const int MAXN = 1e5 + 5, MAXM = 2e5 + 5;
int n, m;  // 节点数、限制数
int head[30][MAXN], nxt[30][MAXM << 1], val[30][MAXM << 1], weight[30][MAXM << 1], idx[MAXM << 1];  // 至多有30个连通块
int color[30][MAXN];
bool vis[30][MAXN];
int cnt = 0;  // 统计连通块中的节点数void add(int pos, int u, int v, int w) {  // 第pos个连通块中建u<->v的权值为w的双向边val[pos][idx[pos]] = v;weight[pos][idx[pos]] = w;nxt[pos][idx[pos]] = head[pos][u];head[pos][u] = idx[pos]++;
}int dfs(int pos, int center, int c) {  // 将第pos个连通块中以center为中心的菊花图染成c色,返回连通块中1的个数color[pos][center] = c;int res = c;  // 统计第pos个连通块中1的个数for (int u = head[pos][center]; ~u; u = nxt[pos][u]) {int v = val[pos][u];if (vis[pos][v]) {  // 出现重边或自环if ((c ^ weight[pos][u]) != color[pos][v]) {  // 检查是否矛盾cout << -1;exit(0);}}else {cnt++;  // 更新该连通块中的节点数vis[pos][v] = true;res += dfs(pos, v, c ^ weight[pos][u]);  // 染以v为中心的菊花图}}return res;
}ll cal(int pos) {  // 求第pos个连通块对答案的贡献ll res = 0;for (int i = 1; i <= n; i++) {if (vis[pos][i]) continue;vis[pos][i] = true;cnt = 1;  // 用于DFS时统计连通块中的节点数int tmp = dfs(pos, i, 1);  // 将连通块中的第一个节点染成1得到的整个连通块中1的个数res += min(tmp, cnt - tmp);  // cnt-tmp是将连通块的第一个节点染成0得到的连通块中1的个数}return res;
}int main() {memset(head, -1, so(head));cin >> n >> m;while (m--) {int u, v, w; cin >> u >> v >> w;for (int i = 0; i < 30; i++) {  // 每个数位维护一个连通块if (w >> i & 1) add(i, u, v, 1), add(i, v, u, 1);  // 建边权为1的边else add(i, u, v, 0), add(i, v, u, 0);  // 建边权为0的边}}ll ans = 0;for (int i = 0; i < 30; i++) ans += cal(i) * (1 << i);  // 统计每个连通块对答案的贡献cout << ans;
}


思路III By : MoYan1082)

对au∧av=wa_u\ ^\wedge a_v=wau​ ∧av​=w,考察www的每个数位:①若该数位为111,则au,ava_u,a_vau​,av​的该数位不同;②若该数位为000,则au,ava_u,a_vau​,av​的该数位相同.

按au,ava_u,a_vau​,av​的对应数位的异同分为两类,用带扩展域的并查集维护:①若aua_uau​和ava_vav​的对应数位相同,则合并fa[u]fa[u]fa[u]和fa[v],fa[u+n]fa[v],fa[u+n]fa[v],fa[u+n]和fa[v+n]fa[v+n]fa[v+n];②若aua_uau​和ava_vav​的对应数位不同,则合并fa[u]fa[u]fa[u]和fa[v+n],fa[u+n]fa[v+n],fa[u+n]fa[v+n],fa[u+n]和fa[v]fa[v]fa[v].

代码IV -> 2021ICPC沈阳-B(并查集)

const int MAXN = 2e5 + 10;
int n, m;  // 节点数、限制数
struct Edge {int u, v, w;
}edges[MAXN];
int fa[MAXN], siz[MAXN];  // 并查集的fa数组、集合的大小int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }void init() {  // 初始化并查集for (int i = 1; i <= n; i++) {fa[i] = i, fa[i + n] = i + n;siz[i] = 1, siz[i + n] = 0;}
}int main() {cin >> n >> m;for (int i = 1; i <= m; i++) cin >> edges[i].u >> edges[i].v >> edges[i].w;ll ans = 0;for (int i = 0; i < 30; i++) {  // 枚举每个数位init();for (int j = 1; j <= m; j++) {int tmpu = find(edges[j].u), tmpv = find(edges[j].v);int tmpun = find(edges[j].u + n), tmpvn = find(edges[j].v + n);if ((edges[j].w >> i) & 1) {  // 该位为1,说明对应位数码不同if (tmpu == tmpv) {  // 矛盾cout << -1;exit(0);}if (tmpu == tmpvn) continue;// 合并tmpu和tmpvn,tmpv和tmpunfa[tmpvn] = tmpu, siz[tmpu] += siz[tmpvn];fa[tmpun] = tmpv, siz[tmpv] += siz[tmpun];}else {  // 该位为0,说明对应位数码相同if (tmpu == tmpvn) {  // 矛盾cout << -1;exit(0);}if (tmpu == tmpv) continue;// 合并tmpu和tmpv,tmpvn和tmpunfa[tmpu] = tmpv, siz[tmpv] += siz[tmpu];fa[tmpun] = tmpvn, siz[tmpvn] += siz[tmpun];}}for (int j = 1; j <= n; j++) {  // 统计每个节点对答案该数位的贡献ans += (ll)min(siz[find(j)], siz[find(j + n)]) * (1 << i);siz[find(j)] = siz[find(j + n)] = 0;}}cout << ans;
}



I. Linear Fractional Transformation (2s2\ \mathrm{s}2 s)

题意

有T(1≤T≤1e5)T\ \ (1\leq T\leq 1\mathrm{e}5)T  (1≤T≤1e5)组测试数据.每组测试数据给定复变函数f(z)=az+bcz+d(a,b,c,d∈C,ad−bc≠0)f(z)=\dfrac{az+b}{cz+d}\ \ (a,b,c,d\in\mathbb{C},ad-bc\neq 0)f(z)=cz+daz+b​  (a,b,c,d∈C,ad−bc=0)经过的不同的三点(zi,wi)(i=1,2,3)(z_i,w_i)\ \ (i=1,2,3)(zi​,wi​)  (i=1,2,3).给定z0z_0z0​,求f(z0)f(z_0)f(z0​).数据保证有唯一解,且∣f(z0)∣<1e6|f(z_0)|<1\mathrm{e}6∣f(z0​)∣<1e6;出现的所有复数的实部、虚部的绝对值都不超过100100100.

有唯一解的解释

分式线性变换是齐次坐标的基变换.在实射影平面π‾\overline{\pi}π上取基底e1→,e2→\overrightarrow{e_1},\overrightarrow{e_2}e1​​,e2​​,取π‾\overline{\pi}π上一点O′O'O′和π‾\overline{\pi}π外一点OOO,则以[OO′→,e1→,e2→][\overrightarrow{OO'},\overrightarrow{e_1},\overrightarrow{e_2}][OO′,e1​​,e2​​]为基底的坐标称为齐次坐标.射影平面上将直线视为点,则若(x1,x2,x3)∈π‾(x_1,x_2,x_3)\in\overline{\pi}(x1​,x2​,x3​)∈π,显然λ(x1,x2,x3)∈π‾(λ≠0)\lambda(x_1,x_2,x_3)\in\overline{\pi}\ \ (\lambda\neq 0)λ(x1​,x2​,x3​)∈π  (λ=0),即齐次坐标只考虑方向,不考虑大小.令λ=1x3\lambda=\dfrac{1}{x_3}λ=x3​1​,则(x1,x2,x3)∼(x1x3,x2x3,1)(x_1,x_2,x_3)\sim \left(\dfrac{x_1}{x_3},\dfrac{x_2}{x_3},1\right)(x1​,x2​,x3​)∼(x3​x1​​,x3​x2​​,1),即R2\mathbb{R}^2R2上的坐标(x,y)(x,y)(x,y)与齐次坐标(x,y,1)(x,y,1)(x,y,1)构成双射.同理,C\mathbb{C}C上的点zzz与齐次坐标(z,1)(z,1)(z,1)构成双射.

思路I

c=0c=0c=0时,f(z)=az+bdf(z)=\dfrac{az+b}{d}f(z)=daz+b​.令d=1d=1d=1,则f(z)=az+bf(z)=az+bf(z)=az+b,两个未知数,代入(z1,w1),(z2,w2)(z_1,w_1),(z_2,w_2)(z1​,w1​),(z2​,w2​)得:{z1a+b=w1z2a+b=w2\begin{cases}z_1a+b=w_1 \\ z_2a+b=w_2\end{cases}{z1​a+b=w1​z2​a+b=w2​​,解得:{a=w1−w2z1−z2b=w1−z1a\begin{cases}a=\dfrac{w_1-w_2}{z_1-z_2} \\ b=w_1-z_1a\end{cases}⎩⎨⎧​a=z1​−z2​w1​−w2​​b=w1​−z1​a​.因已知条件是(zi,wi)(z_i,w_i)(zi​,wi​)而非系数,则通过z3a+b−w3z_3a+b-w_3z3​a+b−w3​是零向量来判定c=0c=0c=0.

c≠0c\neq 0c=0时,令c=1c=1c=1,则f(z)=az+bz+df(z)=\dfrac{az+b}{z+d}f(z)=z+daz+b​,三个未知数,代入(zi,wi)(i=1,2,3)(z_i,w_i)\ \ (i=1,2,3)(zi​,wi​)  (i=1,2,3)得:{z1a+b−w1d=w1z1z2a+b−w2d=w2z2z3a+b−w3d=w3z3\begin{cases}z_1a+b-w_1d=w_1z_1 \\ z_2a+b-w_2d=w_2z_2 \\ z_3a+b-w_3d=w_3z_3\end{cases}⎩⎨⎧​z1​a+b−w1​d=w1​z1​z2​a+b−w2​d=w2​z2​z3​a+b−w3​d=w3​z3​​,用Gauss消元求出a,b,da,b,da,b,d后,代入z0z_0z0​求出f(z0)f(z_0)f(z0​).

总时间复杂度O(Tn3)O(Tn^3)O(Tn3),其中n=3,Tn=3,Tn=3,T最大1e51\mathrm{e}51e5,最坏2.7e62.7\mathrm{e}62.7e6,但常数大.

每组测试数据要输入141414个数,则共需输入1.4e61.4\mathrm{e}61.4e6个数,要用scanf读,实测cin会T.

代码I -> 2021ICPC沈阳-I(Gauss消元)

cp A[5][5];  // 线性方程组的增广矩阵
cp z[5], w[5];  // z_i,w_i
cp z0;  // z_0double get_length2(cp x) { return x.real() * x.real() + x.imag() * x.imag(); }  // 求复数模长的平方void gauss(int n){  // Gauss消元for (int i = 1; i <= n; i++) {int row = i;for (int j = i + 1; j <= n; j++)  // 取出主元最大的一行if (get_length2(A[j][i]) > get_length2(A[row][i])) row = j;if (row != i) swap(A[row], A[i]);  // 将主元最大的行换到上面if (get_length2(A[i][i]) < eps) continue;  // 无需消元for (int j = 1; j <= n; j++) {if (i == j) continue;// 消元cp tmp = A[j][i] / A[i][i];for (int k = i; k <= n + 1; k++) A[j][k] -= A[i][k] * tmp;}}for (int i = 1; i <= n; i++) {if (get_length2(A[i][i]) < eps) continue;A[i][n + 1] /= A[i][i];}
}int main() {CaseT{double x,y;for (int i = 1; i <= 3; i++) {scanf("%lf%lf", &x, &y); z[i] = { x,y };scanf("%lf%lf", &x, &y); w[i] = { x,y };}scanf("%lf%lf", &x, &y); z0 = { x,y };cp a = (w[1] - w[2]) / (z[1] - z[2]), b = w[1] - a * z[1], ans = 0;if (get_length2(a * z[3] + b - w[3]) < eps) ans = a * z0 + b;else {for (int i = 1; i <= 3; i++) A[i][1] = z[i], A[i][2] = { 1,0 }, A[i][3] = -w[i], A[i][4] = w[i] * z[i];gauss(3);ans = (A[1][4] * z0 + A[2][4]) / (z0 + A[3][4]);}printf("%.12lf %.12lf\n", ans.real(), ans.imag());}
}


思路II By : 工口发动机)

设f(zi)=wi(i=0,1,2,3)f(z_i)=w_i\ \ (i=0,1,2,3)f(zi​)=wi​  (i=0,1,2,3).由分式线性变换保交比知:(z0,z1,z2,z3)=(w0,w1,w2,w3)(z_0,z_1,z_2,z_3)=(w_0,w_1,w_2,w_3)(z0​,z1​,z2​,z3​)=(w0​,w1​,w2​,w3​),其中(a,b,c,d)=c−ac−b:d−ad−b(a,b,c,d)=\dfrac{c-a}{c-b}:\dfrac{d-a}{d-b}(a,b,c,d)=c−bc−a​:d−bd−a​,则w2−ww2−w1⋅z3−zz3−z1=w3−ww3−w1⋅z3−zz3−z1\dfrac{w_2-w}{w_2-w_1}\cdot\dfrac{z_3-z}{z_3-z_1}=\dfrac{w_3-w}{w_3-w_1}\cdot\dfrac{z_3-z}{z_3-z_1}w2​−w1​w2​−w​⋅z3​−z1​z3​−z​=w3​−w1​w3​−w​⋅z3​−z1​z3​−z​.

令k1=(z0−z1)(w3−w1)(z3−z2),k2=(z0−z2)(w3−w2)(z3−z1)k_1=(z_0-z_1)(w_3-w_1)(z_3-z_2),k_2=(z_0-z_2)(w_3-w_2)(z_3-z_1)k1​=(z0​−z1​)(w3​−w1​)(z3​−z2​),k2​=(z0​−z2​)(w3​−w2​)(z3​−z1​),整理得w0=k2w1−k1w2k2−k1w_0=\dfrac{k_2w_1-k_1w_2}{k_2-k_1}w0​=k2​−k1​k2​w1​−k1​w2​​.

代码II -> 2021ICPC沈阳-I(推公式)

cp z[5], w[5];  // z_i,w_i
cp z0;  // z_0int main() {CaseT{double x,y;for (int i = 1; i <= 3; i++) {scanf("%lf%lf", &x, &y); z[i] = { x,y };scanf("%lf%lf", &x, &y); w[i] = { x,y };}scanf("%lf%lf", &x, &y); z0 = { x,y };cp k1 = (z0 - z[1]) * (w[3] - w[1]) * (z[3] - z[2]), k2 = (z0 - z[2]) * (w[3] - w[2]) * (z[3] - z[1]);cp ans = (k2 * w[1] - k1 * w[2]) / (k2 - k1);printf("%.12lf %.12lf\n", ans.real(), ans.imag());}
}



M. String Problem (2s2\ \mathrm{s}2 s)

题意

给定一个长度不超过1e61\mathrm{e}61e6的、只包含262626个小写字母的字符串,对每个非空前缀,求其中字典序最大的子串,输出其左右端点在原串中的下标,字符串下标从111开始.

思路

观察样例知:第二列是1,2,3,4,5,⋯1,2,3,4,5,\cdots1,2,3,4,5,⋯,容易想到每个前缀中字典序最大的子串的右端点即该前缀的右端点,若不然,设该前缀是str[1,n]str[1,n]str[1,n],该前缀中字典序最大的子串是s[l,r]s[l,r]s[l,r].考察子串s[l,n]s[l,n]s[l,n],它与s[l,r]s[l,r]s[l,r]前缀相同,但长度>s[l,r]>s[l,r]>s[l,r],故s[l,n]s[l,n]s[l,n]的字典序更大,与s[l,r]s[l,r]s[l,r]的字典序最大矛盾.

先写出O(n2)O(n^2)O(n2)暴力做法:

cin >> str; n = str.length();for (int right = 0; right < n; right++) {  // 枚举右端点int left;  // 左端点string maxstr = "";for (int i = 0; i <= right; i++) {string tmp = str.substr(i, right - i + 1);if (tmp > maxstr) {maxstr = tmp;left = i;}}cout << left + 1 << ' ' << right + 1 << endl;  // 题目下标从1开始
}

考虑如何优化.如上图,设ch2ch_2ch2​是ch1ch_1ch1​之后第一个>ch1>ch_1>ch1​的字符,则ch1ch_1ch1​和ch2ch_2ch2​之间的strstrstr的所有前缀中字典序最大的子串都是以ch1ch_1ch1​开头的,即红色部分的答案的左端点相同,记录后直接跳到下一段的开头ch2ch_2ch2​位置,这样strstrstr只会被遍历一次,时间复杂度O(n)O(n)O(n).该情况iii移动到下一段的开头即i=ki=ki=k.

若遍历到与首字符相同的字符,则需比较前缀来确定哪个子串是字典序最大的.该情况iii移动到下一段的开头是第二个ch1ch_1ch1​的位置,即i=k−(j−i)i=k-(j-i)i=k−(j−i),上述情况也可归为该情况.

注意两种情况iii都需移动到jjj之后,才能保证字符串仅被遍历一次,进而保证O(n)O(n)O(n)的时间复杂度.

注意答案没有被更新过才需要更新,因为之前更新出的答案是子串中最靠左的.

代码 -> 2021ICPC沈阳-M(模拟)

const int MAXN = 1e6 + 5;
string str;
int n;  // 字符串长度
int ans[MAXN];  // 记录字典序最大的子串的左端点int main() {cin >> str; str = " " + str; n = str.length() - 1;int i = 1;while (i <= n) {if(!ans[i]) ans[i] = i;  // 该位置的字典序最大的子串为str[i,i]int j = i, k = i + 1;while (k <= n && str[k] <= str[j]) {if(!ans[k]) ans[k] = i;if (str[k] == str[j]) j++;  // 首字符相同,比较第二个字符else j = i;k++;}while(i <= j) i = k - (j - i);  // i移到j之后的下一段的开头}for (int i = 1; i <= n; i++) cout << ans[i] << ' ' << i << endl;
}



2021ICPC沈阳区域赛BEFIJM相关推荐

  1. 2017 ICPC沈阳区域赛

    2017 沈阳区域赛 题号 题目 难度 知识点 A BBP Formula B Bridge C Empty Convex Polygons D Defense of the Ancients E F ...

  2. icpc 2020沈阳区域赛补题

    2020沈阳区域赛 H 这题是一道典型dp 但是需要用滚动数组优化空间, 在状态转移的时候需要考虑一下是否满足天数条件. 我真的吐了,debug改了半天之后发现错误原因是: 排序应该是 sort(v+ ...

  3. 2021ICPC上海区域赛DEGKI

    题目链接: https://codeforces.com/gym/103446 视频讲解: https://www.bilibili.com/video/bv1994y1f76o 代码:https:/ ...

  4. 记2015沈阳区域赛

    前记 基本确定了要去沈阳和上海打区域赛,听说苏大cp也会沈阳,于是我们的最终目标变为了压苏大. 周五 下午一点左右的飞机,本来想要早上赶概率论作业然后交到学校去,结果睡晚了,干脆就早点去了机场,然后大 ...

  5. 2021ICPC昆明区域赛

    传送门 A 超级大模拟 B 状压dp计数 C D k=0,k=1特判 k为奇数特判 构造112111211111 2^2+ 2^3+ 2^5 F #include<bits/stdc++.h&g ...

  6. 2021ICPC欧洲东南部区域赛题解ACFGJKLN

    2021ICPC欧洲东南部区域赛题解ACFGJKLN A. King of String Comparison 题意 给定两长度为n(1≤n≤2e5)n\ \ (1\leq n\leq 2\mathr ...

  7. 2018ICPC区域赛总结

    2018ICPC区域赛总结 青岛区域赛结束了,本赛季第一个全铁的队伍就此产生. 两场比赛最后都是差一点,还是太菜了.连着两场打铁,说明问题还是在于实力不足. 沈阳站,锅是我的.推公式极具迷惑性的推对了 ...

  8. 退役了,总结的ACM近年区域赛的所有题型

    前面的是退役小记,后面是我个人写近2年所有区域赛场次记录的题型(请忽略我记录的感受),可以留着看下最近的场次名称和原题在哪里有 目录 退役小记(没兴趣可以不看) 这里简单记下我的acm生涯 省赛和三场 ...

  9. 鲲鹏应用创新大赛山西区域赛圆满落幕,鲲鹏生态助力信创变革

    鲲鹏入晋,万里腾飞,8 月 6 日,2021 鲲鹏应用创新大赛山西赛区决赛在太原圆满落幕.今年鲲鹏应用创新大赛区域赛山西赛区是山西省内数字化转型的重要赛事,经过层层选拔,共 35 个队伍进入山西赛区决 ...

最新文章

  1. $\LaTeX$笔记:Section 编号方式(数字、字母、罗马)计数器计数形式修改
  2. NEU 1497 Kid and Ants 思路 难度:0
  3. Mysql_mysql 性能分析及explain用法
  4. 贝叶斯分类器_Sklearn 中的朴素贝叶斯分类器
  5. [Python] 将两个列表合并为字典
  6. 向右键添加新建脚本菜单
  7. Laravel项目问题集锦
  8. 常用的5种数据分析方法有哪些?
  9. BOSS直聘自动投简历
  10. 给已有表添加字段sql
  11. mysql最后一步装不上_mysql安装最后一步 安装不上
  12. UE4 时光倒流(蓝图)
  13. junit忽略测试方法
  14. Mysql 创建表时报错1103:Incorrect table name【问题记录】
  15. USNews:2019世界大学排行榜
  16. 看 Sugar 如何说 I2C 通信
  17. 前后分离与不分离的区别
  18. JSP常用内置对象概述
  19. 涉密计算机的等级分为,涉密人员的涉密等级分为 - 作业在线问答
  20. 史蒂夫·乔布斯(Steve Jobs)——苹果公司CEO

热门文章

  1. 22-07-14 西安 Git 分布式版本控制系统 、代码管理
  2. 《微信公众平台与小程序开发——从零搭建整套系统》——第1章,第1.2节微信公众平台...
  3. 中国移动光猫怎么进网页服务器,中国移动光猫设置方法(192.168.1.1进不了光猫)...
  4. 网页设计初了解-基础知识篇
  5. 基于MATLAB的神经网络进行手写体数字识别(含鼠绘GUI / 数据集:MNIST)
  6. 使用selenium实现前程无忧简历自动刷新
  7. OPC UA - Open62541学习
  8. python之有理数运算Rational类
  9. 方维直播增加三级分销推广功能
  10. 广州电信高级前端开发工程师笔试题及答案(国企面试题大全)