Codeforces Round #779 (Div. 2)
CF1658A Marin and Photoshoot
任意两个 0
之间至少有两个 1
,不够就补上。
char s[MAXN];
int main() {
int T;
read(T);
while(T -- > 0) {
int n;read(n);
scanf("%s" , s + 1);
int ans = 0;
for (int i = 1; i < n; ++i) if(s[i] == '0') {
if(s[i + 1] == '0') ans += 2;
else if(s[i + 2] == '0') ans ++;
}
write(ans);
}
return 0;
}
CF1658B Marin and Anti-coprime Permutation
说真的,很有意思。
人类思维:明显有特解,奇数放偶位,偶数放奇位,观察样例发现很可能是充分必要条件。
于是尝试推广,设 \(\gcd\) 为 \(x\) ,则 \([1,n]\) 中是 \(x\) 倍数的有 \(\lfloor \frac{n}{x} \rfloor\) 个。
然后因此 \(x > 2\) 时我们肯定无法让每个位置都被 \(x\) 整除,于是之前的观察很对。
const int mod = 998244353;
int main() {
int T;
read(T);
while(T -- > 0) {
int n;read(n);
if(n & 1) {
puts("0");
continue;
}
LL ans = 1;
for (int i = 1; i <= n / 2; ++i) ans = ans * i % mod;
ans = ans * ans % mod;
write(ans);
}
return 0;
}
CF1658C Shinju and the Lost Permutation
同样是观察,发现有特殊情况。即当最大值在最前面的时候 b
为 1
。
之后继续循环位移,发现一次最多增加 1
或者减少很多,发现如果满足该性质我一定能通过建立拓扑序构造解,因此又是一个充分必要条件,模拟一下就好。
const int MAXN = 2e5 + 5;
int c[MAXN] , n;
int main() {
int T;
read(T);
while(T -- > 0) {
read(n);
int num = 0;
for (int i = 1; i <= n; ++i) read(c[i]),c[i + n] = c[i] , num += (c[i] == 1);
if(num != 1) {
puts("NO");
continue;
}
for (int i = 1; i <= n; ++i) if(c[i] == 1) num = i;
int fl = 1;
for (int i = 1; i < n; ++i) if(c[i + num] - c[i + num - 1] > 1) {
fl = 0;
break;
}
if(fl) puts("YES");
else puts("NO");
}
return 0;
}
CF1658D
没考虑过 D1
的特别做法,直接做的通解。
\([l,r]\) 是排列的一部分,所以互不相同,异或上一个数后还是互不相同。
考虑我得到的序列,枚举哪个数字是 \(l\) 变化过来的,于是就能枚举到 \(x\) ,由于给定数互不相同,所以异或上 \(x\) 也互不相同,只要保证这些数异或上 \(x\) 的最小值是 \(l\) ,最大值是 \(r\) 就好了。
具体用 01-trie
实现。
const int MAXN = (1 << 17) + 5;
int num , ch[MAXN * 17][21];
void Insert(int x) {
int cur = 1;
for (int i = 16; i >= 0; --i) {
int k = (x >> i) & 1;
if(!ch[cur][k]) ch[cur][k] = ++num;
cur = ch[cur][k];
}
}
int calc(int x , int ty) {
int cur = 1 , ans = 0;
for (int i = 16; i >= 0; --i) {
int k = (x >> i) & 1;
if(ch[cur][k ^ ty]) {
cur = ch[cur][k ^ ty];
ans += (ty) << i;
}
else if(ch[cur][k ^ ty ^ 1]) {
cur = ch[cur][k ^ ty ^ 1];
ans += (ty ^ 1) << i;
}
else break;
}
return ans;
}
int a[MAXN];
int main() {
int T;
read(T);
while(T -- > 0) {
num = 1;int l , r;
read(l),read(r);
for (int i = 1; i <= r - l + 1; ++i) {
read(a[i]);
Insert(a[i]);
}
for (int i = 1; i <= r - l + 1; ++i) {
int x = a[i] ^ l;
if(calc(x , 0) == l && calc(x , 1) == r) {
write(x);
break;
}
}
for (int i = 1; i <= num; ++i) ch[i][0] = ch[i][1] = 0;
}
return 0;
}
CF1658E Gojou and Matrix Game
一个数开始,如果不能走到一个比它大的数,那先手必胜,否则看能否一步走到先手必胜点。
于是从大到小枚举转移,判断能否到达比它大的先手必胜点。
具体用曼哈顿转切比雪夫,用树状数组维护切比雪夫坐标。
const int N = 8000;
const int C = 4000;
int v[2005][2005] , ans[2005][2005];
pii pos[2005 * 2005];
struct BIT {
int tr[N + 5];
void update(int x , int y) {for (; x <= N; x += (x & (-x))) tr[x] += y;}
int find(int x) {int res = 0;for (; x; x -= (x & (-x))) res += tr[x];return res;}
}T[2];
int main() {
int n , k;
read(n),read(k);
for (int i = 1; i <= n; ++i) for (int j = 1; j <= n; ++j) read(v[i][j]) , pos[v[i][j]] = mp(i , j);
for (int i = n * n; i >= 1; --i) {
int X = pos[i].fs + pos[i].sc , Y = pos[i].fs - pos[i].sc + C;
int cur = 0;
if(X - k - 1 >= 1) cur += T[0].find(X - k - 1);
if(X + k + 1 <= N) cur += T[0].find(N) - T[0].find(X + k);
if(Y - k - 1 >= 1) cur += T[1].find(Y - k - 1);
if(Y + k + 1 <= N) cur += T[1].find(N) - T[1].find(Y + k);
if(!cur) ans[pos[i].fs][pos[i].sc] = 1 , T[0].update(X , 1) , T[1].update(Y , 1);
}
for (int i = 1; i <= n; ++i , puts("")) for (int j = 1; j <= n; ++j) {
if(ans[i][j]) putchar('M');
else putchar('G');
}
return 0;
}
CF1658F Juju and Binary String
更加厉害的观察题。
有无解很好判断。观察从每个数开始后面 \(m\) 个数的 1
的个数,记为 \(c_i\)(将序列视为环),则 \(c_i\) 每次变化量为 \(1\)。则必有 \(c_i = m\)。
因此只用最多两步完成构造。
const int MAXN = 4e5 + 5;
int n , m , c[MAXN];
char s[MAXN];
int main() {
int T;
read(T);
while(T -- > 0) {
read(n),read(m);
scanf("%s" , s + 1);
LL num = 0;
for (int i = 1; i <= n; ++i) {
s[i + n] = s[i];
num += (s[i] - '0');
}
if(num * m % n != 0) {
puts("-1");
continue;
}
c[n + 1] = 0;
for (int i = 1; i <= m; ++i) c[n + 1] += s[i] - '0';
for (int i = n; i >= 1; --i) c[i] = c[i + 1] + (s[i] - '0') - (s[i + m] - '0');
for (int i = 1; i <= n; ++i) {
if(c[i] == num * m / n) {
if(i + m - 1 <= n) {
puts("1");
write(i , ' ') , write(i + m - 1);
}
else {
puts("2");
write(1 , ' ') , write(i + m - 1 - n);
write(i , ' ') , write(n);
}
break;
}
}
}
return 0;
}