比赛链接:
https://ac.nowcoder.com/acm/contest/42105
A.Adjacent Swapping
题意:
给定一个字符串 \(s\),要求将它变成 \(xx\)(保证可以分),即拆成两个相同的子串,每次可以交换相邻的两个元素,问最少进行几次操作可以达到目的。
思路:
将整个过程拆成两步,先将字母移到属于自己的区间,然后再考虑让两个字符串相同。
将字母移动到自己的区间,首先要计算每个区间各个字母分别有多少个,将这些字母全部移动到前半部分,剩下的放到后半部分。
接着要将两个字符串变成一样,可以考虑将第一个字符串作为模板串,第一个编号为 1,第二个为 2...以此类推,然后将第二个字符串按照模板串转化掉。
现在,第一个字符串就是 1 2 3... \(n\),第二个字符串是 1 到 \(n\) 的排列,让两个变成一样,即计算逆序对的数量,树状数组或者归并排序都可以实现。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
struct fwt{
int n;
vector <int> a;
fwt(int n) : n(n), a(n + 1) {}
LL sum(int x){
LL res = 0;
for (; x; x -= x & -x)
res += a[x];
return res;
}
void add(int x, LL k){
for (; x <= n; x += x & -x)
a[x] += k;
}
LL query(int x, int y){
return sum(y) - sum(x - 1);
}
};
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n;
string s;
cin >> n >> s;
vector <int> cnt(26);
for (int i = 0; i < n; i ++ )
cnt[s[i] - 'a'] ++ ;
for (int i = 0; i < 26; i ++ )
cnt[i] /= 2;
LL ans = 0;
vector <int> a, b;
for (int i = 0; i < n; i ++ ){
if (cnt[s[i] - 'a']){
cnt[s[i] - 'a'] -- ;
ans += i - a.size();
a.push_back(s[i] - 'a');
}
else b.push_back(s[i] - 'a');
}
vector <int> to(n / 2), pos(26, n / 2);
for (int i = n / 2 - 1; i >= 0; i -- ){
to[i] = pos[a[i]];
pos[a[i]] = i;
}
vector <int> p(26, -1);
for (int i = 0; i < n / 2; i ++ ){
if (p[a[i]] != -1) continue;
p[a[i]] = i;
}
vector <int> c(n / 2);
for (int i = 0; i < n / 2; i ++ ){
c[i] = p[b[i]];
p[b[i]] = to[p[b[i]]];
}
fwt f(n / 2);
for (int i = 0; i < n / 2; i ++ ){
f.add(c[i] + 1, 1);
ans += i + 1 - f.query(1, c[i] + 1);
}
cout << ans << "\n";
return 0;
}
B.Business Website
题意:
\(n\) 个点,满足拓扑图性质,每个点只会到比自己编号大的点,每个点到下一个点的概率已知,问从 1 到其他点的概率是多少。
思路:
点 \(u\) 的概率为 \(x\),到达点 \(v\) 的概率为 \(w\),那么 \(v\) 的概率就会加 \(x * w\),同时 \(u\) 的概率会减去 \(x * w\)。
代码:
#include <bits/stdc++.h>
using namespace std;
void solve(){
int n;
cin >> n;
vector < vector < pair <int, double> > > G(n + 1);
vector <double> ans(n + 1);
vector <int> in(n + 1);
for (int u = 1; u <= n - 1; u ++ ){
int k;
cin >> k;
for (int i = 1; i <= k; i ++ ){
int v;
double w;
cin >> v >> w;
G[u].push_back({v, w});
}
}
ans[1] = 1;
for (int u = 1; u <= n; u ++ ){
double x = ans[u];
for (auto [v, w] : G[u]){
ans[v] += x * w;
ans[u] -= x * w;
}
}
for (int i = 1; i <= n; i ++ )
cout << fixed << setprecision(10) << ans[i] << " \n"[i == n];
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
F.Factor Difference
题意:
\(T\) 组测试,每组告诉 \(n\),要求找到一个正整数,至少有 8 个因数,且每两个因数之间差值至少为 \(n\)。
思路:
因为因数分解后为若干质因数,所以只需要找到最小的三个质因数 + 一个 1,让它们的差值至少为 \(n\),就可以保证至少八个因数了(三个数的组合,就可以产生 4 个数,合起来总共八个)。
通过欧拉筛跑一下,然后求解即可。1 要特判。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int N = 1e6 + 10;
bool st[N];
int prime[N], cnt;
void sieve(int n){
st[1] = true;
for (int i = 2; i <= n; i ++ ){
if (!st[i]) prime[ ++ cnt] = i;
for (int j = 1; j <= cnt && i * prime[j] <= n; j ++ ){
st[i * prime[j]] = true;
if (i % prime[j] == 0) break;
}
}
}
void solve(){
int n;
cin >> n;
if (n == 1){
cout << "24\n";
return;
}
vector <LL> a;
a.push_back(1);
for (int i = 1; i <= cnt; i ++ ){
if (prime[i] >= a.back() + n) a.push_back(prime[i]);
if (a.size() >= 4) break;
}
LL ans = 1;
for (auto x : a)
ans *= x;
cout << ans << "\n";
}
int main(){
sieve(N - 10);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
H.Hacking Interview Solution
题意:
给定 \(m\) 个长为 \(m\) 的序列,问有多少对相同的序列。
思路:
\(map\) 中套一个 \(vector\) 可以过。
也可以对序列进行 \(hash\),然后存起来求解。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
void solve(){
int n, m;
cin >> n >> m;
vector <int> a(n);
for (int i = 0; i < n; i ++ )
cin >> a[i];
map <vector <int>, LL> Map;
for (int i = 0; i < m; i ++ ){
vector <int> o(n);
for (int j = 0; j < n; j ++ )
cin >> o[j];
Map[o] ++ ;
}
LL ans = 0;
for (auto [t, x] : Map)
ans += x * (x - 1) / 2;
cout << ans << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
K.Kooky Clock
题意:
有三条线段,第一条长 \(l_1\),它 \(t_1\) 秒就可以转一圈,第二条长 \(l_2\),它 \(t_2\) 秒就可以转一圈,第三条长 \(l_3\),它 \(t_3\) 秒就可以转一圈。
第一条线段头连着原点,尾连着第二条线段的头,第二条线段的尾连着第三条线段,现在经过了 \(T\) 秒(线段顺时针转),问第三条线段的尾的坐标。
思路:
用三角函数。
思路:
#include <bits/stdc++.h>
using namespace std;
const double pi = acos(-1);
int main(){
double T;
cin >> T;
vector <double> l(3), t(3);
for (int i = 0; i < 3; i ++ )
cin >> l[i];
for (int i = 0; i < 3; i ++ )
cin >> t[i];
double x = 0, y = 0;
for (int i = 0; i < 3; i ++ )
x += l[i] * sin(T / t[i] * 2 * pi);
for (int i = 0; i < 3; i ++ )
y += l[i] * cos(T / t[i] * 2 * pi);
cout << fixed << setprecision(10) << x << " " << y << "\n";
return 0;
}