A
题目
这个 A 题为什么是平时 B 题的分值?
统计每一个字母的出现次数,找到出现次数为 1 1 1 的字母,输出它的位置,完事。
AC Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
string s;
int cnt[100100];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s;
int len = s.size();
for (int i = 0; i < len; i++) {
cnt[s[i] - 'a']++;
}
for (int i = 0; i < 26; i++) {
if (cnt[i] == 1) {
for (int j = 0; j < len; j++) {
if (s[j] == i + 'a') {
cout << j + 1;
return 0;
}
}
}
}
return 0;
}
B
题目
对于每一个寻问,找到这两个数的位置,输出值较小的那个数的坐标,完事。
AC Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
int n, q;
int p[100100];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i];
}
cin >> q;
while (q--) {
int l ,r;
cin >> l >> r;
int idx1, idx2;
for (int i = 1; i <= n; i ++) {
if (p[i] == l) idx1 = i;
if (p[i] == r) idx2 = i;
}
cout << (idx1 < idx2 ? l : r) << '\n';
}
return 0;
}
C
题目
此题容易被误解成并查集,但是第一个样例就可以 hack 普通并查集做法。
我们直接记录原来字串中那些字母要被替换成另外的字母,一开始所有字母都要被替换成自己,每出现一个替换关系,比如把 a a a 替换成 b b b,我们就寻找原来要替换成 a a a 的字母,将其替换成 b b b,最后输出时在原字母中进行替换即可。
AC Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
int n, q;
string s;
int f[30];
int main() {
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
cin >> n >> s;
for (int i = 0; i < 26; i++) f[i] = i;
cin >> q;
while (q--) {
string u, v;
cin >> u >> v;
int a = u[0] - 'a', b = v[0] - 'a';
for (int i = 0; i < 26; i++) if (f[i] == a) f[i] = b;
}
for (int i = 0; i < n; i ++) cout << char('a' + f[s[i] - 'a']);
return 0;
}
D
题目
对于一个完全平方数,我们将其分解质因数,每一个质因数的个数一定是偶数,这个不难理解。
那么如果我们将一个完全平方数乘上另一个完全平方数,结果是完全平方数,这个应该也在你的理解范围里。
根据上一个定理,我们可以推导出:如果一个完全平方数除以另一个完全平方数,如果结果是整数,那么结果也是一个完全平方数。
现在,关键的定理来了:
如果两个数 a a a 和 b b b,他们可以分解为 k ⋅ p k\cdot p k⋅p 和 l ⋅ q l \cdot q l⋅q,其中 k , p , l , q k,p,l,q k,p,l,q 为整数,且 p p p 和 q q q 为完全平方数, p p p 和 q q q 尽可能大,如果 a ⋅ b a\cdot b a⋅b 为完全平方数,那么 k k k 等于 l l l。
让我们证明一下,对于结果,除以 p p p 和 q q q 后,剩下 k ⋅ l k\cdot l k⋅l,那么结果一定是一个完全平方数,这个定理可以根据上面说的推导出来。而 k k k 和 l l l 不是完全平方数,只能说明 k k k 等于 l l l。请自行理解,如果 k k k 不等于 l l l,哪里会产生矛盾。
我们就可以预处理出每一个数分解掉所有可被分解的完全平方数,统计剩下每一个数字的个数。对于每一个数字,可以与其他和这个数字相等的组成一对,加上其他数字的个数。对于 0 0 0,加上 2 ( n − 1 ) 2(n-1) 2(n−1),因为这个 0 0 0 可以与其他数字组成一组,其他数字也可以与这个数字组成一组。设一共有 x x x 个 0 0 0,最后答案要减去 x ( x − 1 ) x(x-1) x(x−1),因为 0 0 0 与 0 0 0 之间的连边被重复算了。最后答案除以 2 2 2,去掉重复算的组数,输出就完事了。
特别注意:答案要开长整形!
AC Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
long long n, a[200100];
long long ans;
map<long long, long long> cnt;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (long long i = 1; i <= n; i ++) cin >> a[i];
for (long long i = 1; i <= n; i ++) {
long long tmp = sqrt(a[i]);
bool vis = 1;
while (vis) {
vis = 0;
for (long long j = 2; j <= tmp; j++) {
if (a[i] % (j * j) == 0) {
a[i] /= (j * j);
vis = 1;
break;
}
}
}
}
for (long long i = 1; i <= n; i ++) {
cnt[a[i]]++;
}
for (long long i = 1; i <= n; i ++) {
if (a[i] == 0) ans += (n - 1) * 2;
else ans += cnt[a[i]] - 1;
}
ans -= cnt[0] * (cnt[0] - 1);
cout << ans / 2;
return 0;
}
E
题目
要算每一个点到终点的时间,可不可以把所有边逆转方向,再从终点跑一遍最短路?
我们得到在时间限制下每一条路线最后一班车的时间,再跑一遍迪杰斯特拉算法,终点的时间为无穷大,然后让每一个点到达的时间尽量大,去找下一条边时,要乘坐时间不能超过当前时间的列车。如果第一班车都比当前时间晚,就不能乘坐。如果没有跑到一个点,就说明这个点不能到达终点。
注意:要开长整形,由于精度问题,建议全开长整形。
AC Code:
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <list>
#include <set>
#include <map>
using namespace std;
long long n, m;
struct edge{
long long u, v, w, l, d, k, nxt;
long long get(long long t) {
if (t < l + w) return -1;
return l + min(k - 1, (t - l - w) / d) * d;
}
};
edge ed[400100];
long long edcnt, head[200100];
void addedge(long long l, long long d, long long k, long long u, long long v, long long w){
edcnt++;
ed[edcnt].u = u;
ed[edcnt].v = v;
ed[edcnt].w = w;
ed[edcnt].l = l;
ed[edcnt].d = d;
ed[edcnt].k = k;
ed[edcnt].nxt = head[u];
head[u] = edcnt;
}
struct node {
int x;
long long dis;
node(int x_, long long dis_) {
x = x_;
dis = dis_;
}
};
bool operator <(node a, node b) {
return a.dis < b.dis;
}
long long dis[514114];
long long ans[200100];
void dijkstra() {
priority_queue<node> pq;
pq.push(node(n, 0x7f7f7f7f7f7f7f7f));
while (!pq.empty()) {
node now = pq.top();
pq.pop();
if (ans[now.x] > now.dis) continue;
for (int i = head[now.x]; i; i = ed[i].nxt) {
int v = ed[i].v;
long long t = ed[i].get(now.dis);
if (t != -1 && ans[v] < t) {
ans[v] = t;
pq.push(node(v, t));
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
long long l, d, k, c, a, b;
cin >> l >> d >> k >> c >> a >> b;
addedge(l, d, k, b, a, c);
}
for (long long i = 1; i <= n; i++) {
ans[i] = -1;
}
ans[n] = 0x7f7f7f7f7f7f7f7f;
dijkstra();
for (int i = 1; i < n; i++) {
if (ans[i] != -1) cout << ans[i] << '\n';
else cout << "Unreachable\n";
}
return 0;
}