可惜赛时被 E 恶心死了,过了 E 就开摆了。
A.Goals of Victory
题目描述:
有 n n n 支球队参加足球比赛。每对球队对阵一次。每场比赛结束后,Pak Chanek 会收到两个整数作为比赛结果,即两队在比赛中的进球数。一支球队的效率等于该队在每场比赛中的总进球数减去对手在每场比赛中的总进球数。
比赛结束后,Pak Dengklek 会统计每支球队的效率。结果他忘了其中一支球队的效率。给定 n − 1 n-1 n − 1 支球队的效率 a 1 , a 2 , a 3 , … , a n − 1 a_1,a_2,a_3,\ldots,a_{n-1} a 1 , a 2 , a 3 , … , a n − 1 。求缺少的那支队伍的效率是多少?可以证明,缺失队伍的效率是唯一确定的。
2 ≤ n ≤ 100 2 \le n \le 100 2 ≤ n ≤ 1 0 0 ,− 100 ≤ a i ≤ 100 -100 \le a_i \le 100 − 1 0 0 ≤ a i ≤ 1 0 0
题目分析:
通过手玩样例以及手摸可以发现,答案就等于:
− ∑ i = 1 n − 1 a i -\sum_{i=1}^{n-1} a_i
− i = 1 ∑ n − 1 a i
代码:
点击查看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <bits/stdc++.h> using namespace std;const int N = 1005 ;int a[N];int main () { int T;scanf ("%d" ,&T); while (T--){ int n;scanf ("%d" ,&n); int sum = 0 ; for (int i=1 ; i<n; i++) scanf ("%d" ,&a[i]),sum += a[i]; printf ("%d\n" ,-sum); } return 0 ; }
B.Helmets in Night Light
题目描述:
Pak Chanek 是一个名叫坤甸(Khuntien)的村庄的村长。在一个灯火通明的夜晚,Pak Chanek 突然接到一个重要通知,需要通知坤甸村的所有 n n n 居民。
首先,Pak Chanek 将公告直接发给一位或多位居民,每人的费用为 p p p 。之后,居民可以使用一个神奇的头盔状装置将公告分享给其他居民。但是,使用头盔形装置需要付费。如果第 i i i 个居民已经至少得到过一次公告(直接从 Pak Chanek 那里或从其他居民那里),他/她最多可以将公告分享给 a i a_i a i 个其他居民,每次分享 的成本为 b i b_i b i 。
如果 Pak Chanek 也可以控制居民向其他居民分享公告的方式,那么 Pak Chanek 通知坤甸所有 n n n 居民公告的最小成本是多少?
1 ≤ n ≤ 1 0 5 1 \le n \le 10^5 1 ≤ n ≤ 1 0 5
题目分析:
我们肯定是把消息每次分享给 b b b 尽可能小的,因为以后由他们继续向其他人分享的成本是最低的。
所以可以直接将所有的人按 b b b 排序,从小到大分享消息,注意就是如果当前最优的 b b b 都不如 p p p 优,那么显然直接用 p p p 的花费即可,可以使用双指针维护最优的位置。
代码:
点击查看代码
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 #include <bits/stdc++.h> #define int long long #define PII pair<int,int> using namespace std;const int N = 1e5 +5 ;bool cmp (PII a,PII b) { if (a.second != b.second) return a.second < b.second; return a.first > b.first; } PII a[N]; bool flag[N];signed main () { int T;scanf ("%lld" ,&T); while (T--){ int n,p;scanf ("%lld%lld" ,&n,&p); for (int i=1 ; i<=n; i++) scanf ("%lld" ,&a[i].first); for (int i=1 ; i<=n; i++) scanf ("%lld" ,&a[i].second); sort (a+1 ,a+n+1 ,cmp); int pos = 1 ,ans = 0 ; for (int i=1 ; i<=n; i++){ while (flag[pos] && !a[pos].first) ++pos; if (flag[pos] && a[pos].second <= p) ans += a[pos].second,a[pos].first--; else if (flag[pos] && a[pos].second > p) ans += p; if (!flag[pos]){ ans += p; } flag[i] = true ; } printf ("%lld\n" ,ans); for (int i=1 ; i<=n; i++) flag[i] = false ; } return 0 ; }
C.Joyboard
题目描述:
游戏少年 Chaneka 发明了一种名为 joyboard 的新型游戏控制器。有趣的是,她发明的游戏板只能用来玩一种游戏。
游戏板有一个屏幕,上面有 n + 1 n+1 n + 1 个插槽,从左到右依次为 1 1 1 到 n + 1 n+1 n + 1 。n + 1 n+1 n + 1 个插槽将被一个 [ a 1 , a 2 , a 3 , … , a n + 1 ] [a_1,a_2,a_3,\ldots,a_{n+1}] [ a 1 , a 2 , a 3 , … , a n + 1 ] 的序列填满。玩家 Chaneka 必须在 a n + 1 a_{n+1} a n + 1 中指定一个介于 0 0 0 和 m m m 之间的整数。然后,对于从 n n n 到 1 1 1 的每个 i i i ,a i a_i a i 的值将等于 a i + 1 a_{i+1} a i + 1 (右边相邻的值)除以 i i i 的余 。换句话说,就是 a i = a i + 1 m o d i a_i = a_{i + 1} \bmod i a i = a i + 1 m o d i 。
Chaneka 希望每个插槽都分配一个整数后,整个屏幕(所有 n + 1 n+1 n + 1 插槽中)正好有 k k k 个不同的值。将一个非负整数赋值给插槽 n + 1 n+1 n + 1 的有效方法有多少种?
1 ≤ n ≤ 1 0 9 1 \le n \le 10^9 1 ≤ n ≤ 1 0 9 ,0 ≤ m ≤ 1 0 9 0 \le m \le 10^9 0 ≤ m ≤ 1 0 9 ,1 ≤ k ≤ n + 1 1 \le k \le n+1 1 ≤ k ≤ n + 1
题目分析:
考虑我们这个过程是什么样的,首先 m m m 会模 n n n 也就是会变成一个小于 n n n 的数 x x x ,在我们从大到小遍历的时候,如果当前没有遍历到 x x x 那么显然所有的数都大于 x x x 也就是取模之后没有影响,当我们遍历到 x x x 时就会直接变成 0 0 0 ,所以整个段就是一个类似 0 − x − m 0-x-m 0 − x − m 的样子。
也就是说如果 k > 3 k > 3 k > 3 则必然无解,只需要考虑 k ≤ 3 k \le 3 k ≤ 3 的情况。
如果 k = 1 k = 1 k = 1 也就是必然满足 x = 0 x = 0 x = 0 且 m = 0 m = 0 m = 0 ,也就是只有一种情况。
如果 k = 2 k = 2 k = 2 也就是必然满足 [x = 0 x = 0 x = 0 且 m ≠ x m \not= x m = x ] 或者 [x ≠ 0 x \not= 0 x = 0 且 x = m x = m x = m ],也就是 [ 1 , n ] [1,n] [ 1 , n ] 加上 ( n , m ] (n,m] ( n , m ] 中 n n n 的倍数。
如果 k = 3 k = 3 k = 3 其实就是 ( n , m ] (n,m] ( n , m ] 中不是 n n n 的倍数的数。
代码:
点击查看代码
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 #include <bits/stdc++.h> #define int long long using namespace std;signed main () { int T;scanf ("%lld" ,&T); while (T--){ int n,m,k;scanf ("%lld%lld%lld" ,&n,&m,&k); if (k > 3 ) printf ("0\n" ); else if (k == 3 ){ int l = n + 1 ,r = m; if (l > r) printf ("0\n" ); else printf ("%lld\n" ,(r-l+1 ) - ((r/n) - ((l-1 )/n))); } else if (k == 2 ){ int l = 1 ,r = min (n,m); if (l > r) printf ("0\n" ); else { int tmp = m / n - n / n; printf ("%lld\n" ,r-l+1 +(tmp > 0 ? tmp : 0 )); } } else if (k == 1 ){ printf ("%lld\n" ,1ll ); } } return 0 ; }
D.Effects of Anti Pimples
题目描述:
Chaneka 有一个数组 [ a 1 , a 2 , … , a n ] [a_1,a_2,\ldots,a_n] [ a 1 , a 2 , … , a n ] 。最初,所有元素都是白色的。Chaneka 将选择一个或多个不同的位置,并将这些位置上的元素染成黑色。然后,她会选择所有位置是至少一个黑色元素位置倍数的白色元素,并将这些元素染成绿色。之后,她的得分就是所有黑色和绿色元素中 a i a_i a i 的最大值。
假设当前选择了位置 i i i 变成黑色,则位置 i , 2 i , 3 i , ⋯ i,2i,3i,\cdots i , 2 i , 3 i , ⋯ 里为白色元素的位置均会被染成绿色。
Chaneka 有 2 n − 1 2^n-1 2 n − 1 种方法来选择黑色指数。求 Chaneka 选择黑色位置的所有可能方式的得分之和。由于答案可能很大,请打印答案的模数 998 244 353 998\,244\,353 9 9 8 2 4 4 3 5 3 。
1 ≤ n ≤ 1 0 5 1 \le n\le 10^5 1 ≤ n ≤ 1 0 5 ,1 ≤ a i ≤ 1 0 5 1\le a_i \le 10^5 1 ≤ a i ≤ 1 0 5
题目分析:
其实就是对于所有黑色的位置,下标为这个位置倍数的位置均被染绿,其实将染绿看作染黑也没有任何问题。
可以考虑钦定某个位置就是最大值,这样的话限制就是相当于:比它大的数的位置的所有约数都不能被选择,它的位置的约数至少有一个被选择。
可以直接从大到小枚举最大值,然后增量地维护出可以选择的位置是哪些,因为枚举约数可以实现 O ( n ) O(\sqrt{n}) O ( n ) ,所以复杂度为 O ( n n ) O(n \sqrt{n}) O ( n n ) 。
对于值相同的位置,我们可以钦定位置更靠后的更大就好了。
代码:
点击查看代码
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> #define int long long #define PII pair<int,int> using namespace std;const int N = 2e5 +5 ;const int MOD = 998244353 ;PII a[N]; int pw[N];bool flag[N];bool cmp (PII a,PII b) { return a.first > b.first; } signed main () { int n;scanf ("%lld" ,&n); pw[0 ] = 1 ; for (int i=1 ; i<=n; i++) pw[i] = pw[i-1 ] * 2 % MOD; for (int i=1 ; i<=n; i++) scanf ("%lld" ,&a[i].first),a[i].second = i; sort (a+1 ,a+n+1 ,cmp); int sz = n; int ans = 0 ; for (int i=1 ; i<=n; i++){ int tmp = 0 ; for (int j=1 ; j*j<=a[i].second; j++){ if (a[i].second % j != 0 ) continue ; if (!flag[j]) ++tmp; if (j * j != a[i].second && !flag[a[i].second/j]) ++tmp; } ans = (ans + a[i].first * (pw[tmp]-1 )%MOD * pw[sz-tmp]%MOD)%MOD; for (int j=1 ; j*j<=a[i].second; j++){ if (a[i].second % j != 0 ) continue ; if (!flag[j]) flag[j] = true ,--sz; if (!flag[a[i].second/j]) flag[a[i].second/j] = true ,--sz; } } printf ("%lld\n" ,(ans%MOD+MOD)%MOD); return 0 ; }
E.Autosynthesis
题目描述:
Chaneka 写下了一个由 n n n 个正整数元素组成的数组 a a a 。最初,所有元素都没有圈起来。在一次操作中,Chaneka 可以圈出一个元素。同一元素可以圈定多次。
完成所有操作后,Chaneka 按照下标顺序,将 a a a 中所有未被圈出的元素组成一个序列 r r r 。
Chaneka 还制作了另一个序列 p p p ,其长度等于所执行的操作次数,并且 p i p_i p i 是在第 i i i 次操作中被圈出的元素的下标 。
Chaneka 希望进行若干次操作,使序列 r r r 等于序列 p p p 。请帮助她实现这个目标,如果不可能,请报告!请注意,如果有多个解决方案,您可以输出其中任何一个。
1 ≤ n ≤ 2 ⋅ 1 0 5 1 \le n \le 2\cdot 10^5 1 ≤ n ≤ 2 ⋅ 1 0 5 ,1 ≤ a i ≤ n 1 \le a_i \le n 1 ≤ a i ≤ n
题目分析:
为了方便,下文如果元素如果被留在序列 r r r 中我们称其为被选择。
看到是下标和值之间的相等,所以一个想法就是从 i i i 连向 a i a_i a i ,这样的话如果 i i i 被选择则 a i a_i a i 一定不被选择,因为如果选择 a i a_i a i 就找不到一个数能与 i i i 匹配。
因为一个命题的逆否命题与其是等价的,所以如果 a i a_i a i 被选择,则 i i i 一定不被选择,也就是说对于建出来的这棵基环树如果节点 i i i 被选择,则与它相邻的所有节点都不能被选择。
同理如果节点 i i i 不被选择,则连向 i i i 的点里至少有一个被选择,不然的话 i i i 也没有匹配的点了。
这个东西一看就是可以 d p dp d p 解决的,我们可以对于基环树断环为链然后钦定好相邻的这两个点是否选择,然后做 d p dp d p 就好了,只不过细节有亿点点多,反正我是调了一个多小时才过。
具体的 d p dp d p 状态就是设 d p [ i ] [ 0 / 1 ] dp[i][0/1] d p [ i ] [ 0 / 1 ] 表示考虑 i i i 为根的子树,i i i 是否被选择的情况下,是否存在一种解。
转移的话就是根据上面的这个限制转移就好了。
代码:
点击查看代码
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 #include <bits/stdc++.h> using namespace std;const int N = 2e5 +5 ;bool dp[N][2 ],vis[N],flag[N];vector<int > son[N]; int fa[N],pos1,pos2,opt1,opt2,a[N],tmp;void get_dp (int now,int fath) { vis[now] = true ; for (auto to : son[now]){ if (to == fath || to == pos1) continue ; get_dp (to,now); } dp[now][1 ] = 1 ,dp[now][0 ] = 0 ; for (auto to : son[now]){ if (to == fath || to == pos1) continue ; dp[now][1 ] &= dp[to][0 ]; dp[now][0 ] |= dp[to][1 ]; } for (auto to : son[now]){ if (to == fath || to == pos1) continue ; if (!dp[to][1 ] && !dp[to][0 ]){ dp[now][0 ] = 0 ; } } if (now == pos2){ dp[now][0 ] |= (opt1 == 1 ); dp[now][opt2^1 ] = 0 ; } } void get (int now) { dp[now][1 ] = 1 ,dp[now][0 ] = 0 ; for (auto to : son[now]){ dp[now][1 ] &= dp[to][0 ]; dp[now][0 ] |= dp[to][1 ]; } for (auto to : son[now]){ if (!dp[to][1 ] && !dp[to][0 ]){ dp[now][0 ] = 0 ; } } dp[now][1 ] &= dp[fa[now]][0 ]; } void get_ans (int now,int fath,int opt) { flag[now] = opt; if (opt == 0 ){ for (auto to : son[now]){ if (to == fath || to == pos1) continue ; if (to == pos2) get_ans (to,now,opt2); else if (dp[to][1 ]) get_ans (to,now,1 ); else get_ans (to,now,0 ); } } else if (opt == 1 ){ for (auto to : son[now]){ if (to == fath || to == pos1) continue ; if (to == pos2) get_ans (to,now,opt2); else get_ans (to,now,0 ); } } } int main () { int n;scanf ("%d" ,&n); for (int i=1 ; i<=n; i++){ scanf ("%d" ,&a[i]); son[a[i]].push_back (i); fa[i] = a[i]; } int ans = true ; for (int i=1 ; i<=n; i++){ if (!vis[i]){ int res = 0 ; int now = i; while (!vis[now]){ vis[now] = true ; now = fa[now]; } pos1 = now,pos2 = fa[now]; opt1 = 1 ,opt2 = 0 ; get_dp (now,0 ); get (now); if (dp[now][opt1] && dp[pos2][opt2]){ get_ans (pos1,0 ,opt1); res = true ; } opt1 = 0 ,opt2 = 1 ; get_dp (now,0 );get (now); if (!res && dp[now][opt1] && dp[pos2][opt2]){ get_ans (pos1,0 ,opt1); res = true ; } opt1 = 0 ,opt2 = 0 ; get_dp (now,0 );get (now); if (!res && dp[now][opt1] && dp[pos2][opt2]){ get_ans (pos1,0 ,opt1); res = true ; } if (!res) ans = false ; } } if (ans){ int tot = 0 ; for (int i=1 ; i<=n; i++) tot += flag[i]; printf ("%d\n" ,tot); for (int i=1 ; i<=n; i++){ if (flag[i]) printf ("%d " ,a[i]); } } else printf ("-1\n" ); return 0 ; }
F.Lexichromatography
题目描述:
Pak Chanek 热爱他所在的系–印度尼西亚大学(Fasilkom)计算机科学系。他想利用系徽的颜色–蓝色和红色–进行创作。
数组 a a a 由 n n n 个元素组成,元素 i i i 的值为 a i a_i a i 。Pak Chanek 希望给数组中的每个元素涂上蓝色或红色,从而满足以下条件:
有多少种不同的颜色满足所有这些条件呢?由于答案可能非常大,请打印答案的模数 998 244 353 998\,244\,353 9 9 8 2 4 4 3 5 3 。当且仅当至少有一个元素在一种着色中是蓝色,而在另一种着色中是红色时,两种着色才是不同的。
设 p p p 和 q q q 是两个不同的序列。当且仅当 p p p 是 q q q 的前缀,或者存在一个位置 i i i ,使得 p j = q j p_j=q_j p j = q j 对每个 1 ≤ j < i 1\leq j<i 1 ≤ j < i 和 p i < q i p_i<q_i p i < q i 都成立时,序列 p p p 在字典序上小于序列 q q q 。尤其是,空序列在字典序上总是小于任何非空序列。
题目分析:
首先可以考虑一个个限制来看,看看可以发现什么性质。
对于第一个限制,可以发现其实限制严格大于并不简单,而因为严格小于是与它本质相同的一个问题,所以可以使用总方案数 2 n 2^n 2 n 减去两者完全相同的方案数,最后除以 2 2 2 就是答案。
对于第二个限制,就是对于值相同的元素其按下标排序之后染色必然是红蓝交替的,也就是 …红蓝红蓝红…
可以发现如果要使得两个序列相同,也就是说在任意时刻均满足某一个序列是另一个序列的前缀,所以他们相同的部分我们其实是不用管的,我们只需要在意长的那个串多出来的部分即可。
当我们第一次插入某个元素的时候,因为较长串内必然没有和它匹配的位置,所以我们只能插入到较长串内,而第二次插入的时候因为上一次插入到了较长串内这一次就不能插入了,就只能插入较短串内与上一次插入匹配。
以此类推,我们在奇数次插入某个元素的时候必然插入到较长串的末尾,而偶数次则必然插入到较短串的末尾与对应的位置匹配,所以我们可以唯一地确定每个元素该插入到哪个串中。
而因为在同一个串中仅仅只是颜色相同并未指定哪种颜色,所以可以我们就是相当于可以把元素划分成若干个集合,每个集合内颜色必须相同,设有 m m m 个集合,答案其实就是 2 n − 2 m 2^n - 2^m 2 n − 2 m 。
以及在操作的过程中也要时刻判断是否合法,如果不合法也就是不存在相等的方案,答案就是 2 n 2^n 2 n 。
最终的答案不要忘记除以 2 2 2 啊。
代码:
点击查看代码
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 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 2e6 +5 ;const int MOD = 998244353 ;int fa[N],a[N],cnt[N];int power (int a,int b) { int res = 1 ; while (b){ if (b & 1 ) res = res * a % MOD; a = a * a % MOD; b >>= 1 ; } return res; } int find (int x) { if (fa[x] == x) return x; return fa[x] = find (fa[x]); } signed main () { int n;scanf ("%lld" ,&n); for (int i=1 ; i<=n; i++) scanf ("%lld" ,&a[i]); deque<int > q; for (int i=1 ; i<=200000 ; i++) fa[i] = i; bool flag = true ; for (int i=1 ; i<=n; i++){ cnt[a[i]]++; if (cnt[a[i]] % 2 == 0 && q.front () != a[i]) flag = false ; if (!q.empty () && q.front () == a[i]) q.pop_front (); else { if (!q.empty ()) fa[find (q.front ())] = find (a[i]); q.push_back (a[i]); } } int ans1 = 0 ,ans2 = 0 ; for (int i=1 ; i<=200000 ; i++){ if (cnt[i]) ++ans1; if (cnt[i] && find (i) == i) ++ans2; if (cnt[i] & 1 ) flag = false ; } if (!flag) printf ("%lld\n" ,power (2 ,ans1-1 )); else printf ("%lld\n" ,(power (2 ,ans1-1 ) - power (2 ,ans2-1 ) + MOD)%MOD); return 0 ; }