比赛传送门
A. Divide and Conquer
题意:给你一个数组,每次操作可以将一个数变为它除以二下取整,求将数组的和变为偶数的最小次数。
显然如果数组本来就是偶数,则为 0 0 0 ,否则一定是选一个数一直除到改变,而其他数不动(动了显然更劣)。于是对于每个数求改变奇偶的最小次数,模拟即可。
By jiangly
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 #include <bits/stdc++.h> using i64 = long long ;void solve () { int n; std::cin >> n; std::vector<int > a (n) ; int par = 0 ; int mn = 1E9 ; for (int i = 0 ; i < n; i++) { std::cin >> a[i]; par ^= a[i] & 1 ; int x = a[i], t = 0 ; while (x % 2 == a[i] % 2 ) { x /= 2 ; t++; } mn = std::min (mn, t); } std::cout << (par ? mn : 0 ) << "\n" ; } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
B. Make Array Good
题意:有一个数组,每次可以将一个数 x x x 改为 [ x , 2 x ] [x,2x] [ x , 2 x ] 内的任意整数,求在 n n n 次以内将这个数组改为任意两个数均有倍数关系的方案。
由于在 [ x , 2 x ] [x,2x] [ x , 2 x ] 内,容易想到让它们均变为二的幂(此时一定合法)。于是对于每个数改为比他大的最近的二的幂即可。可以使用 2 ⌊ log 2 ( n ) ⌋ + 1 2^{\lfloor\log_2(n)\rfloor+1} 2 ⌊ l o g 2 ( n ) ⌋ + 1 来快速得出。
By jiangly
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 #include <bits/stdc++.h> using i64 = long long ;void solve () { int n; std::cin >> n; std::vector<int > a (n) ; std::cout << n << "\n" ; for (int i = 0 ; i < n; i++) { std::cin >> a[i]; int p = 1 << (std::__lg(a[i]) + 1 ); std::cout << i + 1 << " " << p - a[i] << "\n" ; } } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
C. Binary Strings are Fun
给你一个 01 01 0 1 串,对于每个前缀,计算生成合法串的方案数,输出它们的和(对 998244353 998244353 9 9 8 2 4 4 3 5 3 取模)。生成一个合法串,需要在每相邻两位之间插入一个新位,使得任意一个奇数前缀的中位数为末尾的数字。
首先举例子找一找规律。假设待生成的串为 0_0_1_1_0_1_1_1
,下划线是需要填的。我们依此考虑:只考虑前三位,中位数为 0 0 0 ,所以第一个空填 01 01 0 1 都可以;考虑前五位,中位数为 1 1 1 ,所以第一个和第二个空都必须填 1 1 1 ;前七位,第四个空 01 都可以;前九位,则第三个和第四个空只能填 0 0 0 。
依此类推,我们发现,如果某一个“限制位置”和下一个“限制位置”不同,那么中位数在这两位后发生改变,所以这两位之间的空位必须填后面的值,且前面这两位的数量差只能为 1 1 1 。如果某一个限制位置和下一个限制位置相同,那么必须填与它们相反的值,因为如果与它们相同,则数量差超过 1 1 1 ,而后面一个不同的位置就会无法填。
所以,只有一个位置后面没有不同位置时,才可以真正的任意填,否则只有一种方案。此时答案就很显然了,即为 2 2 2 的(最后一段连续的长度 − 1 -1 − 1 )次方。
By jiangly
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 void solve () { int n; std::cin >> n; std::string s; std::cin >> s; int last = 0 ; Z ans = 0 , sum; for (auto c : s) { if (last != c) ans = 1 ; else ans *= 2 ; last = c; sum += ans; } std::cout << sum << "\n" ; } int main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); int t; std::cin >> t; while (t--) { solve (); } return 0 ; }
D. GCD Queries
题意:交互题。有一个 0 ∼ n − 1 0\sim n-1 0 ∼ n − 1 的排列,每次可以询问两位的 gcd \gcd g cd ,在 2 n 2n 2 n 次操作内给出两个 位置,使得它们中的一个位置为 0 0 0 。
首先有一个非常重要的性质:∀ y , gcd ( x , 0 ) ≥ gcd ( y , 0 ) \forall y,\gcd(x,0)\ge \gcd(y,0) ∀ y , g cd( x , 0 ) ≥ g cd( y , 0 ) 。所以我们可以依此扫描每个位置,维护当前扫到的可能为 0 0 0 的两个位置 x , y x,y x , y 。
初始时 x = 1 , y = 2 x=1,y=2 x = 1 , y = 2 ,从 3 3 3 开始,对于每个位置 i i i ,询问 gcd ( p x , p i ) \gcd(p_x,p_i) g cd( p x , p i ) 和 gcd ( p y , p i ) \gcd(p_y,p_i) g cd( p y , p i ) ,如果两个答案相同,那么 p i p_i p i 不可能是 0 0 0 (因为 gcd ( 0 , x ) = x \gcd(0,x)=x g cd( 0 , x ) = x ,而排列里 x x x 互不相同);如果两个答案不同,那么 x , y x,y x , y 中较小的那个一定不是 0 0 0 (因为前面的性质),而 p i p_i p i 有可能是 0 0 0 ,将较小的那个改为 i i i 即可。
By cxm1024
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 <bits/stdc++.h> using namespace std;signed main () { int t; cin>>t; while (t--) { int n; cin>>n; int now=2 ,x=1 ,y=2 ,xx,yy; while (now<n) { now++; cout<<"? " <<x<<" " <<now<<endl; cin>>xx; cout<<"? " <<y<<" " <<now<<endl; cin>>yy; if (xx>yy) y=now; else if (yy>xx) x=now; } cout<<"! " <<x<<" " <<y<<endl; cin>>n; assert (n==1 ); } return 0 ; }