wtf

WTF is white tight feet.

  1. 1. B. Build Roads
  2. 2. C. Cat Virus
  3. 3. D. Dyson Box
  4. 4. F. Birthday Cake

马上省赛,为了应付一下这个考试,得看看往年题,总结一下经验。

B. Build Roads

一个王国,有n个城市。想建n-1条边连接城市,第i个城市有一个建筑公司经验值为ai。想在i和j城市之间建一条路,需要两个城市的建筑公司互相竞争,在修路中两家公司会起冲突,从而造成材料浪费。浪费材料为gcd(ai,aj)

请设计一条路连接n个城市用n条边,花费最少。

1
2
3
int gen() {		// a[i] = gen()
return xorshitf64() % (R - L + 1) + L;
}

分析:如果R = L,那么ai = L,相邻城市之间花费L,总花费L*(n-1)。如果R ≠ L,n较小时暴力连边建生成树(n^2);如果n较大,若R - L + 1较大,根据素数分布,素数相距大概几百左右,这样L到(R - L + 1) + L之间必然有一个素数,有一个数为素数,gcd就为1,答案为(n - 1),若R - L + 1较小,R - L + 1区间之内会被取满,根据连续的两个数必然互质可得结果仍然为(n - 1)。

暴力建树,单向建边,取其中任意n-1条边一定会把n个城市给连在一起。所以不需要并查,直接排序依据最小权取n-1个即可。

1
2
3
4
5
6
7
8
9
vector<int> e;
int ans = 0;
for (int i = 1; i <= n; i++) a[i] = gen();
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
e.push_back(__gcd(a[i], a[j]));
sort(e.begin(), e.end());
for (int i = 0; i <= n - 1; i++) ans += e[i];
// output ans
1
2
3
4
// 当n较大时
ans = n - 1;
// L = R时
ans = (n - 1) * L;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include<bits/stdc++.h>
using namespace std;
using ull = unsigned long long;
ull n, L, R, seed, a[200002];
ull xorshift64() {
ull x = seed;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return seed = x;
}
int gen() {
return xorshift64() % (R - L + 1) + L;
}
int main(void) {
ull ans = 0;
cin >> n >> L >> R >> seed;
if (L == R) ans = (n - 1) * L;
else if (n <= 20) {
vector<ull> e;
for (int i = 1; i <= n; i++) a[i] = gen();
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++)
e.push_back(__gcd(a[i], a[j]));
sort(e.begin(), e.end());
for (int i = 0; i < n - 1; i++) ans += e[i];
} else {
ans = n - 1;
}
cout << ans << "\n";
return 0;
}

C. Cat Virus

一个国家,有很多族可以被视为有根树。一个族包含黑色和白色,改族新生儿可能是黑色,如果一个新生儿是黑色,那么它的后代也是黑色。

给一个整数k,可以有k种方式标记黑色和白色组成家族成员。两种方式肯定有不完全相同的成员,至少有一个黑色,而另一个为白色。

问如何建一个有根树使得方式为k。

设f(u)为染色u及u所有子树的方案树,若将u染成黑色,则只有一种方案,若将u染成白色,则设u的子节点为v,f(u)=proc(f(v))+1,u方案数为子树方案数之积在加上黑色的一种方案。各子树之间互不影响,所以是乘积。

若k为奇数,我们给k弄一个左儿子和右儿子,向右儿子方向走,f(r) = (k - 1) / 2,f(l) = 2, f(u) = f(l) * f(r) + 1,若k为偶数,我们直接让u连接一个子节点,然后到下一层,方案数边成k - 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include<bits/stdc++.h>
using namespace std;
#define int long long
using ull = unsigned long long;
int k;
vector<pair<int, int>> ans;
int res = 0;
void dfs(int idx, int pw) { // idx-序号,pw-方案数
if (pw < 3) return;
if (pw == 3) {
ans.push_back(make_pair(idx, idx + 1));
res++;
return;
}
if (pw & 1) {
ans.push_back(make_pair(idx, idx + 1));
ans.push_back(make_pair(idx, idx + 2));
res += 2;
dfs(idx + 2, pw >> 1);
} else {
ans.push_back(make_pair(idx, idx + 1));
res++;
dfs(idx + 1, pw - 1);
}
}
signed main(void) {
cin >> k;
if (k == 2) {
cout << "1" << "\n"; return (0);
} else if (k == 3) {
cout << "2\n1 2\n"; return (0);
}
else dfs(1, k);
cout << res + 1 << "\n";
for (auto &i: ans) {
cout << i.first << " " << i.second << "\n";
}
return (0);
}

D. Dyson Box

一个盒子二维方格,左下角坐标为(0,0),右上角坐标为(2×10 ^ 5,2×10 ^ 5)。

将有n个事件发生,第i次事件时出现一个立方体,左下角坐标为(xi-1,yi-1),右上角坐标为(xi,yi)。

盒子里有两个地心引力的方向,水平和竖直,有一半的可能是水平,剩下一半是竖直。她开始测量所有立方体轮廓的总长。如果引力方向是水平,所有立方体水平向左移动,如果是竖直,则竖直向下移动。

计算每次事件后两种情况(两种情况是指向左移动或向下移动)的轮廓总长。

对于竖直情况,当已经有一块,添加下一块时要减去2,对于旁边的两列,如果旁边两栋大楼更高,就减2。

xxx

image-20220518200737208

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <bits/stdc++.h>
#define int long long
#define debug(x) cout << "[*]" << #x << " = " << x << "\n";
using namespace std;
const int maxn = 2e5 + 5;
int n, a[maxn], b[maxn];
int ans_x, ans_y;
signed main(void) {
cin >> n;
while (n--) {
int x, y;
cin >> x >> y;
if (a[x]) ans_x -= 2;
a[x]++;
if (a[x] <= a[x - 1]) ans_x -= 2;
if (a[x] <= a[x + 1]) ans_x -= 2;
ans_x += 4;
if (b[y]) ans_y -= 2;
b[y]++;
if (b[y] <= b[y - 1]) ans_y -= 2;
if (b[y] <= b[y + 1]) ans_y -= 2;
ans_y += 4;
// debug(b[x]);
// debug(b[x + 1]);
// debug(b[x - 1]);
cout << ans_x << " " << ans_y << "\n";
}
return (0);
}

F. Birthday Cake

请帮助Yamabuki分蛋糕,Yamabuki有n种不同的蛋糕,可以用小写拉丁字母表示,表示蛋糕上面有什么,字符串的拼接表示蛋糕的合并。

若AB满足CC结构,(A,B两个字符串拼接后可以拆成两个相同的字符串即满足此规律),若AB满足,则BA也满足,所以枚举A的时候找可以满足条件的B即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;
const int mod[2] = {999999929,1000000007};
const int maxn = 4e5+5;
const int sz = 233;
using ll = long long;
string a[maxn];
ll Hase[maxn][2];
ll fac[maxn][2];
map<pair<ll,ll>,int> mp;
ll length(ll now, int small, int mod) {
return (now * sz + small) % mod;
}
ll fetch(int l, int r, int idx) {
return (Hase[r][idx]-Hase[l-1][idx]*fac[r-l+1][idx]%mod[idx]+mod[idx])%mod[idx];
}
int main()
{
ios::sync_with_stdio(false);
fac[0][0] = fac[0][1] = 1;
for(int i = 1;i <= 400000;++i){
fac[i][0] = fac[i-1][0]*sz%mod[0];
fac[i][1] = fac[i-1][1]*sz%mod[1];
}
int n;
cin>>n;
for(int i = 1;i <= n;++i){
cin>>a[i];
}
sort(a+1,a+1+n,[](const string&a,const string &b)->bool{
return a.size()<b.size();
});
ll ans = 0;
for(int i = 1;i <= n;++i){
ll val[2] = {0,0};
for(int j = 0;j < a[i].size();++j){
val[0] = length(val[0], a[i][j], mod[0]);
Hase[j+1][0] = val[0];
val[1] = length(val[1], a[i][j], mod[1]);
Hase[j+1][1] = val[1];
}
ans += mp[make_pair(val[0],val[1])];
int len = a[i].size();
for(int j = 1;j <= len/2;++j){
if (fetch(1, j, 0) == fetch(len - j + 1, len, 0) &&
fetch(1, j, 1) == fetch(len - j + 1, len, 1)) {
ans += mp[make_pair(fetch(j + 1, len - j, 0), fetch(j + 1, len - j, 1))];
}
}
mp[make_pair(val[0],val[1])]++;
}
cout<<ans<<endl;
return 0;
}

本文作者 : wtfff
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议(CC BY-NC-SA 4.0)进行许可。This blog is under a CC BY-NC-SA 4.0 Unported License
本文链接 : http://im0use.github.io/2022/05/19/acm_solve/

本文最后更新于 天前,文中所描述的信息可能已发生改变