A. Captain Flint and Crew Recruitment #构造 题意 定义一类正整数,能够被p ∗ q p*q p ∗ q 表示,其中p 、 q ( 1 < p < q ) p、q(1<p<q) p 、 q ( 1 < p < q ) 均为素数,称之为n e a r l y p r i m e nearly\ prime n e a r l y p r im e 。现要求判断整数n n n ,是否能被4个不同整数之和 表示,且其中至少三个整数为n e a r l y p r i m e nearly\ prime n e a r l y p r im e (是则,输出YES否则输出NO)
分析 n = 31 = 2 × 7 + 2 × 5 + 2 × 3 + 1 n = 31 = 2\times7+2\times5+2\times3+1 n = 31 = 2 × 7 + 2 × 5 + 2 × 3 + 1 ,其中14 , 10 , 6 14,10,6 14 , 10 , 6 为n e a r l y p r i m e nearly\ prime n e a r l y p r im e
n = 32 = 2 × 3 + 2 × 5 + 2 × 7 + 2 n = 32 = 2\times3+2\times5+2\times7+2 n = 32 = 2 × 3 + 2 × 5 + 2 × 7 + 2 ,其中14 , 10 , 6 14,10,6 14 , 10 , 6 为n e a r l y p r i m e nearly\ prime n e a r l y p r im e
容易得到最小的三个n e a r l y p r i m e nearly\ prime n e a r l y p r im e 为6 , 10 , 14 6,10,14 6 , 10 , 14 ,因而满足条件的最小整数为31,见上。当n ≤ 30 = 6 + 10 + 14 n\leq30=6+10+14 n ≤ 30 = 6 + 10 + 14 ,答案NO。当n > 30 n>30 n > 30 ,答案定为YES
难道剩余的正整数都能被6 , 10 , 14 , n − 30 6,10,14,n-30 6 , 10 , 14 , n − 30 表示吗?注意,当n − 30 = 6 o r 10 o r 14 n-30 = 6or10or14 n − 30 = 6 or 10 or 14 时,出现了重复数字,我们可以构造出比{2 ∗ 3 , 2 ∗ 5 , 2 ∗ 7 , n 2*3,2*5,2*7,n 2 ∗ 3 , 2 ∗ 5 , 2 ∗ 7 , n }稍大的组合,即{2 ∗ 3 , 2 ∗ 5 , 3 ∗ 5 , n 2*3,2*5,3*5,n 2 ∗ 3 , 2 ∗ 5 , 3 ∗ 5 , 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 #include <algorithm> #include <cstdio> #include <cstring> using namespace std;typedef long long ll;int main () { int Q; scanf ("%d" , &Q); while (Q--) { int n; scanf ("%d" , &n); if (n <= 30 ) { printf ("NO\n" ); continue ; } int last = n - 30 ; if (last == 6 || last == 10 || last == 14 ) { printf ("YES\n6 10 15 %d\n" , n - 31 ); } else { printf ("YES\n6 10 14 %d\n" , n - 30 ); } } return 0 ; }
B. Captain Flint and a Long Voyage #构造 #贪心 题意 给定整数n n n ,找出一个十进制数字x x x 满足:长度为n n n ,得到x x x 在二进制下的串,去掉后面的n n n 位,新二进制串是最大的,且数字x x x 越小越好。好绕,原题面看得十分吃力
分析 我们将[ 0 , 9 ] [0, 9] [ 0 , 9 ] 数字分别转化为二进制串,可以观察到数字8 , 9 8,9 8 , 9 的二进制串长度最长,分别为1000 , 1001 1000, 1001 1000 , 1001 ,由题干要求,抹除n n n 位后的二进制串最大,那么利用贪心思想,要找到这样的串,那么十进制数字x x x 的二进制串越长越好,于是我们可以断定这个**x x x 肯定由8 o r 9 8or9 8 or 9 组成**。
但别忘了题干还要求我们数字x x x 越小越好,为什么可以有这样的条件?拿数字8 , 9 8,9 8 , 9 的二进制串1000 , 1001 1000, 1001 1000 , 1001 而言,当我们抹除最后1位时,得到的两个新串是相等的!于是但凡得到数字999...999 999...999 999...999 ,我们可以在合法的长度内将末位的几个9 9 9 替换为8 8 8 ,这样既保证二进制串大且原十进制数小的情况了,即**x x x 的组成肯定为999...888 999...888 999...888 **。
如何确定这一合法的替换长度呢?考虑到8/9二进制串长度为4。对于原数字x x x 最末尾的最末尾的(向上取整)n / 4 n/4 n /4 个要被去除的串,选8还是9的二进制串是没有区别的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <algorithm> #include <cstdio> #include <cstring> using namespace std;typedef long long ll;int main () { int Q; scanf ("%d" , &Q); while (Q--){ int n; scanf ("%d" , &n); int len = n / 4 ; if (n % 4 != 0 ) len++; for (int i = 1 ; i <= n - len; i++) printf ("9" ); for (int i = 1 ; i <= len; i++) printf ("8" ); printf ("\n" ); } return 0 ; }
C. Uncle Bogdan and Country Happiness #DFS #逆向思维 题意 给定n n n 个节点的树结构,m m m 个人初始在根节点1 1 1 号,已知每个人都有各自要到达的节点(默认走最短路),他们刚出发有各自的心情状态(好/坏)且我们未知,在节点之间的转移过程中好心情会转化为坏心情 ,但心情坏的不会发生改变。先给定每个节点数据h [ i ] h[i] h [ i ] ,理论上值为该节点上 心情好的人数 - 心情坏的人数,现要判断给定数据是否正确。
分析 若从节点1出发来考虑似乎不容易,我们可以尝试从每个到达的目标节点逆推到根节点。
设经过 城市n o w now n o w 人数有s u m [ n o w ] sum[now] s u m [ n o w ] ,该城市理论快乐指数为h [ n o w ] h[now] h [ n o w ] ,可以计算出理论上该城市的快乐人数
H a p s u m [ n o w ] = ( s u m [ n o w ] + h [ n o w ] ) / 2 Hapsum[now] =(sum[now]+h[now])/2 H a p s u m [ n o w ] = ( s u m [ n o w ] + h [ n o w ]) /2
= ( H a p s u m [ n o w ] + b a d s u m [ n o w ] + H a p s u m [ n o w ] − b a d s u m [ n o w ] ) = (Hapsum[now]+badsum[now]+Hapsum[now]-badsum[now]) = ( H a p s u m [ n o w ] + ba d s u m [ n o w ] + H a p s u m [ n o w ] − ba d s u m [ n o w ])
参考官方题解,我们有三个标准去衡量数据的合法性:
s u m [ n o w ] + h [ n o w ] sum[now]+h[now] s u m [ n o w ] + h [ n o w ] 一定能被整除0 ≤ H a p s u m [ n o w ] ≤ s u m [ n o w ] 0\leq Hapsum[now]\leq sum[now] 0 ≤ H a p s u m [ n o w ] ≤ s u m [ n o w ] 即快乐人数肯定不会超过总人数H a p s u m [ t o 1 ] + H a p s u m [ t o 2 ] + . . . + H a p s u m [ t o k ] ≤ H a p s u m [ n o w ] Hapsum[to_1]+Hapsum[to_2]+...+Hapsum[to_{k}]\leq Hapsum[now] H a p s u m [ t o 1 ] + H a p s u m [ t o 2 ] + ... + H a p s u m [ t o k ] ≤ H a p s u m [ n o w ] 即每个人从now出发可能会心情变糟,因而子节点的快乐人数总数一定不超过父节点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 #include <algorithm> #include <cstdio> #include <cstring> using namespace std;typedef long long ll;const int MAXN = 1e5 + 5 ;struct Edge { int to, nextNbr; }E[MAXN << 1 ];int Head[MAXN], tot = 0 ;void addEdge (int u, int v) { tot++; E[tot].to = v; E[tot].nextNbr = Head[u]; Head[u] = tot; }int n, m, delt[MAXN], cnt[MAXN], sum[MAXN], HapSum[MAXN];bool f = true ;void dfs (int pre, int now) { if (f == false ) return ; sum[now] = cnt[now]; int tmp_sum = 0 ; for (int i = Head[now]; i >= 0 ; i = E[i].nextNbr) { int v = E[i].to; if (v != pre) { dfs (now, v); sum[now] += sum[v]; tmp_sum += HapSum[v]; } } if ((sum[now] + delt[now]) % 2 != 0 ) f = false ; HapSum[now] = (sum[now] + delt[now]) >> 1 ; if (HapSum[now] < 0 || HapSum[now] > sum[now]) f = false ; if (HapSum[now] < tmp_sum) f = false ; }int main () { int Q; scanf ("%d" , &Q); while (Q--) { scanf ("%d%d" , &n, &m); tot = 0 ; f = true ; for (int i = 1 ; i <= n; i++) scanf ("%d" , &cnt[i]); for (int i = 1 ; i <= n; i++) scanf ("%d" , &delt[i]), Head[i] = -1 ; for (int i = 1 ; i <= n - 1 ; i++) { int x, y; scanf ("%d%d" , &x, &y); addEdge (x, y); addEdge (y, x); } dfs (0 , 1 ); printf ("%s\n" , f ? "YES" : "NO" ); } return 0 ; }