2021.7.7 校内模拟赛游记
先来 T1 大模拟镇楼。
点击展开看代码
1 #include <iostream>
2 #include <string>
3 #include <algorithm>
4 #include <cstdio>
5 const std::string D = "GSBQ";
6 std::string erase(std::string s, int l, int r) {
7 if (l > r) return s;
8 return s.substr(0, l) + s.substr(r+1, s.size()-r-1);
9 }
10 struct twt {
11 std::string org, Z, F, ans;
12 bool isPos, isZer;
13 twt() { isPos = 1; isZer = 0; org = Z = F = ans = ""; }
14 void opt() {
15 int cntn = 0, pos = 0;
16 for (int i = 0; i < (signed)org.size() && org[i] == '+'
17 || org[i] == '-'; i++)
18 cntn += org[i] == '-', pos ++;
19 if (cntn % 2 == 0) isPos = 1;
20 else isPos = 0;
21 org = org.substr(pos, org.size() - pos);
22 }
23 void div() {
24 int pos = -1;
25 for (int i = 0; i < (signed)org.size(); i++)
26 if (org[i] == '.') { pos = i; break; }
27 Z = org.substr(0, pos);
28 if (pos != -1 && pos != (signed)org.size() - 1)
29 F = org.substr(pos+1, org.size()-pos-1);
30 }
31 void prez() {
32 int pos = 0;
33 for (int i = 0; i < (signed)Z.size() && Z[i] == '0'; i++)
34 pos ++;
35 Z = Z.substr(pos, Z.size() - pos);
36 }
37 void sucz() {
38 int pos = (signed)F.size() - 1;
39 for (int i = (signed)F.size() - 1; i >= 0 && F[i] == '0'; i--)
40 pos --;
41 F = F.substr(0, pos + 1);
42 }
43 std::string printUnit(std::string t) {
44 std::string an;
45 if (t == "0000") return "";
46 if (t[0] != '0') an += t[0];
47 bool proz = 0;
48 for (int i = 1; i < (signed)t.size(); i++)
49 if (t[i] != '0') {
50 if (!proz) an = an + D[i] + t[i];
51 else proz = 0, an = an + "0" + D[i] + t[i];
52 }
53 else proz = 1;
54 if (proz) an = an + "0";
55 return an;
56 }
57 std::string fix(std::string x) {
58 bool doit = 1;
59 while (doit) {
60 int pos = -1;
61 for (int i = 0; i < (signed)x.size()-1; i++)
62 if (x[i] == '0' && (x[i+1] == 'Q' ||
63 x[i+1] == 'B' ||
64 x[i+1] == 'S')) pos = i;
65 if (pos == -1) doit = 0;
66 else x = erase(x, pos, pos+1);
67 }
68
69 int pos = (signed)x.size() - 1;
70 for (int i = (signed)x.size() - 1; i >= 0 && x[i] == '0'; i--)
71 pos --;
72 x = erase(x, pos+1, x.size()-1);
73
74 for (int i = 0; i < (signed)x.size()-1; i++)
75 if (x[i] == '0' && x[i+1] == 'W') {
76 x = erase(x, i, i);
77 break;
78 }
79
80 for (int i = 0; i < (signed)x.size()-1; i++)
81 if (x[i] == '0' && x[i+1] == 'Y') {
82 x = erase(x, i, i);
83 break;
84 }
85 return x;
86 }
87 void print() {
88 std::reverse(Z.begin(), Z.end());
89 std::string an;
90 if (Z.size() == 0) an += "0";
91 else if (Z.size() > 4) {
92 an += printUnit(Z.substr(0, 4)) + "W";
93 if (Z.size() > 8) {
94 if (Z.substr(4, 4) != "0000") an += printUnit(Z.substr(4, 4));
95 else an = erase(an, an.size()-1, an.size()-1);
96 an += "Y" + printUnit(Z.substr(8, Z.size() - 8));
97 }
98 else an += printUnit(Z.substr(4, Z.size()-4));
99 }
100 else an += printUnit(Z.substr(0, Z.size()));
101
102 std::reverse(an.begin(), an.end());
103 // std::cout << an;
104 an = fix(an);
105
106 if (F.size()) an = an + ((!an.size()) ? "0" : "") + "D" + F;
107 if (!Z.size() && !F.size()) isZer = 1;
108 ans = an;
109 }
110 void doit(std::string x) {
111 org = x;
112 opt(), div(), prez(), sucz(), print();
113 }
114 };
115 struct dwd {
116 twt m, s;
117 std::string t;
118 bool isM;
119 dwd () { isM = 0; }
120 void read() {
121 std::cin >> t;
122 int pos = -1;
123 for (int i = 0; i < (signed)t.size(); i++)
124 if (t[i] == '/') { pos = i; break; }
125 if (pos == -1) s.doit(t);
126 else s.doit(t.substr(0, pos)), isM = 1,
m.doit(t.substr(pos+1, t.size()-pos-1));
127 }
128 void print() {
129 bool isNeg = m.isPos ^ s.isPos;
130 if (s.ans == "" || s.ans == "0") return std::cout << "0", void();
131 if (m.ans == "1") isM = 0;
132 if (isNeg) std::cout << "F";
133 if (isM) std::cout << m.ans << "fz";
134 std::cout << s.ans;
135 }
136 } a;
137 int main() {
138 freopen("read.in", "r", stdin);
139 freopen("read.out", "w", stdout);
140
141 std::ios::sync_with_stdio(false);
142 a.read(), a.print();
143 return 0;
144 }
145
146 // 0156Z - Coding completed
147 // 0156Z - Before submit checklist completed
T1
给定一个实数,读出它!
大模拟,代码都在上面了。
这次的思路还是比较清晰的,但还是有一些情况没有考虑到,用 2h 写完,后来只得了 \(76\) 分,因为没有判只有一个点的情况以及去掉多余 \(0\) 时没考虑到“亿”。写了两个小时主要是因为前面还是楼下了一些情况,导致调试了很久,如果想得更明白一些还是有望在一小时半之内 A 掉的。
下次遇到这样的题再加油吧。
T2
给定一张网格图,每次删条边,看边的俩端点在删掉是否仍然联通。强制在线。
如果离线的话做法应该还是很好想的,把所有输入都存下来然后倒着连边,用并查集判断是否想通。
但赛场上没有仔细看数据范围,没去写离线做法。感觉暴力做又挺难写的,才 \(20\) 分,所以直接去看下一题了。
赛后发现其实听好做的。
把网格图的联通转换成中间格子的联通,易得一条边删掉了不连通当且仅当相邻的两个中间格子在此之前就已经联通了,相当于断开的吧这俩点围起来了。
挺妙的,不过赛时确实没有往这个地方想。
T3
给出一张图,有负权。权值有两份。从起点出发到终点走,若走在一条不是一张图从边起点到终点的最短路上,该图会报警,求最小报警数。
赛场上感觉非常好做,而且数据范围 \(70\) 分的“保证数据随机”非常的诱惑人,仿佛只要写一个暴力就可以得到 \(70\) 的分数一样。
然后赛场上写了却连大样例都没过,调而来一回儿后果断 skip procedues,去写 T4 了,然后 T4 回来又继续调了未果,浪费了大量时间,最后发现……原来非负权的部分要建双向边。。。
正确做法据说很套路但感觉对于我还是难以想到的。
因为双向边部分没有负权,可以使用更快的 Dijkstra, 而且缩掉强联通分量后就是 DAG, 所以可以边拓扑排序边跑 Dijkstra, 这样就可以用 Dijkstra 来保证时间复杂度。
代码有那么一点难写。
展开看代码
#include <cstdio>
#include <queue>
#include <utility>
#include <algorithm>
#include <cstring>
const int N = 1500005;
const long long INF = 0x3f3f3f3f3f3f3f3fll;
inline char gc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
// #define gc getchar
inline int read(){
char c=gc();int tot=1,sum=0;
while ((c<'0'|| c>'9')&&c!='-') c=gc();
if (c=='-'){tot=-1;c=gc();}
while (c>='0'&&c<='9'){sum=sum*10+c-'0';c=gc();}return sum*tot;
}
// #undef gc
int n, m, w, head[N], next[N], vet[N], val1[N], val2[N], S, T, u,
v, w1, w2, num;
long long dis[N];
struct tup { int x, y, z; };
void add(int u, int v, int w1, int w2) {
vet[++num] = v, val1[num] = w1, val2[num] = w2;
next[num] = head[u];
head[u] = num;
}
struct dwd {
int fa[N];
void init(int n) {
for (int i = 1; i <= n; i++) fa[i] = i;
}
int find(int x) { return (x == fa[x]) ? x : (fa[x] = find(fa[x])); }
void merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
fa[x] = y;
}
} ufs;
std::priority_queue<std::pair<int, int> > q;
struct twt {
long long dis[N];
int in[N];
bool vis[N];
std::vector<std::pair<int, int> > E[N];
std::vector<tup> gout[N], gin[N];
std::priority_queue<std::pair<int, int> > q;
void add(int u, int v, int w) { E[u].push_back(std::make_pair(v, w)); }
void add1(int u, int v, int w) {
tup an;
an.x = u, an.y = v, an.z = w;
gout[ufs.find(u)].push_back(an);
gin[ufs.find(v)].push_back(an);
in[ufs.find(v)] ++;
}
void dij() {
while (!q.empty()) {
int u = q.top().second;
q.pop();
for (int i = 0; i < (signed)E[u].size(); i++) {
int v = E[u][i].first, l = E[u][i].second;
if (dis[u] + l < dis[v]) {
dis[v] = dis[u] + l;
q.push(std::make_pair(-dis[v], v));
}
}
}
}
void Dijkstra(int s) {
memset(dis, 0x3f, sizeof dis);
dis[s] = 0;
q.push(std::make_pair(0, s));
dij();
}
void topo() {
std::queue<int> Q;
for (int i = 1; i <= n; i++)
if (!in[ufs.fa[i]] && !vis[ufs.fa[i]])
Q.push(ufs.fa[i]), vis[ufs.fa[i]] = 1;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = 0; i < (signed)gout[u].size(); i++) {
int v = ufs.fa[gout[u][i].y];
in[v] --;
if (in[v] == 0) {
for (int j = 0; j < (signed)gin[v].size(); j++) {
dis[gin[v][j].y] = std::min(dis[gin[v][j].y],
dis[gin[v][j].x] + gin[v][j].z);
q.push(std::make_pair(-dis[gin[v][j].y], gin[v][j].y));
}
dij();
Q.push(v);
}
}
}
}
} G1, G2;
void getans() {
q.push(std::make_pair(0, S));
memset(dis, 0x3f, sizeof dis);
dis[S] = 0;
while (!q.empty()) {
int u = q.top().second;
q.pop();
for (int i = head[u]; i; i = next[i]) {
int v = vet[i], w1 = val1[i], w2 = val2[i];
int l = (G1.dis[u] - G1.dis[v] != w1) +
(G2.dis[u] - G2.dis[v] != w2);
if (dis[u] + l < dis[v]) {
dis[v] = dis[u] + l;
q.push(std::make_pair(-dis[v], v));
}
}
}
}
signed main() {
freopen("gpsduel.in", "r", stdin);
freopen("gpsduel.out", "w", stdout);
n = read(), m = read(), w = read(), S = read(), T = read();
ufs.init(n);
for (int i = 1; i <= m; i++) {
u = read(), v = read(), w1 = read(), w2 = read();
G1.add(u, v, w1), G1.add(v, u, w1);
G2.add(u, v, w2), G2.add(v, u, w2);
add(u, v, w1, w2), add(v, u, w1, w2);
ufs.merge(u, v);
}
G1.Dijkstra(T), G2.Dijkstra(T);
for (int i = 1; i <= w; i++) {
u = read(), v = read(), w1 = read(), w2 = read();
G1.add1(v, u, w1), G2.add1(v, u, w2), add(u, v, w1, w2);
}
G1.topo(), G2.topo();
// for (int i = 1; i <= n; i++) printf("%lld ", G1.dis[i]); puts("");
getans();
if (G1.dis[S] == INF) puts("inf inf -1");
else printf("%lld %lld %lld", G1.dis[S], G2.dis[S], dis[T]);
return 0;
}
T4
定义
twt-sort就是将一个字符串中下标模 \(d\) 是 \(0\) 的拿出来依次放头,再把是 \(1\) 的拿出来依次放头……。给定一个字符串,每次输入 \(k, d\), 输出每次对于所有长 \(k\) 的区间从左到右依次进行完
twt-sort的结果。
妙妙题!赛场上只想到写暴力。。。
这个 twt-sort 其实相当于一个转置。
然后有个绝妙的想法!把整个字符进行一次操作的转置求出来(\(k\) 个之后的都转到原来不动),然后乘以一格左移的转置。这样进行 \(n-k+1\) 次操作就是不停地对整个字符串操作后在右移回来。因为这个操作有结合律,所以可以快速幂优化。
代码。
#include <iostream>
#include <string>
const int N = 1000005;
int a[N], an[N], tmp[N], k, d, p[N], l[N], n, m;
std::string st, ans;
void mul(int *a, int *b) {
for (int i = 0; i < n; i++) tmp[i] = a[b[i]];
for (int i = 0; i < n; i++) a[i] = tmp[i];
}
void Pow(int *a, int b) {
for (int i = 0; i < n; i++) an[i] = i;
for ( ; b; b >>= 1, mul(a, a))
if (b & 1) mul(an, a);
for (int i = 0; i < n; i++) a[i] = an[i];
}
int main() {
// std::ios::sync_with_stdio(false);
std::cin >> st;
std::cin >> m;
n = st.size();
ans = st;
for (int i = 0; i < n; i++) l[i] = i+1;
l[n-1] = 0;
while (m--) {
k = read(), d = read();
int kk = 0, c = 0;
while (kk < d) {
int i = kk;
while (i < k) p[c++] = i, i += d;
kk++;
}
for (int i = k; i < n; i++) p[i] = i;
// for (int i = 0; i < n; i++) printf("%d ", p[i]); puts("");
mul(p, l);
int t = n - k + 1;
Pow(p, t);
for (int i = 0; i < n; i++) ans[i] = st[p[(i >= t) ? (i-t) : (i+n-t)]];
std::cout << ans << '\n';
st = ans;
}
return 0;
}
卡卡常就过了
好吧还有一个线性的做法。把每个转置看成有向图连边,然后在把左移的边连上,这样必然构成不超过 \(O(n)\) 规模的环,把环都找出来,看看一个应该在哪里就可以了。
但是我懒没有写

浙公网安备 33010602011771号