AtCoder Regular Contest 127
A - Leading 1s
史上最阴间的 ARC A 题,全场过的人 900 个不到。。。
设 \(f_i\) 表示有 \(i\) 个前导 \(1\) 的数字的个数,发现直接求不好算,先算至少有 \(i\) 个前导 \(1\) 的数字个数,然后再容斥一下减去所有 \(f_j \ (j > i)\) 即可。
i64 n, g[20], bin[29];
int main() {
scanf("%lld", &n);
i64 bit = 1, m = 0;
while(bit * 10ll <= n) bin[++m] = (n / bit) % 10, bit *= 10ll;
bin[++m] = (n / bit) % 10;
int pos = m, fx = 0;
while(bin[pos] == 1) pos -- ;
fx = bin[pos];
i64 res = 0;
form(i,m,1) {
rep(j,0,m - i) g[i] += q_pow(10ll, j);
if(pos >= m - i + 1) {
if(fx == 1) {forn(j,1,m - i) g[i] += bin[j] * q_pow(10ll, j - 1);g[i] ++ ;}
else if(fx) g[i] += q_pow(10ll, m - i);
} else {
i64 mul = 1ll;
forn(j,1,m - i) mul += 1ll * (bin[j]) * q_pow(10ll, j - 1);
g[i] += mul;
}
forn(j,i + 1,m) g[i] -= g[j];
res += 1ll * i * g[i];
}
printf("%lld\n", res);
return 0;
B - Ternary Strings
构造的方案显然是对于第一位是 \(2\) 的数,从小到大去取出前 \(N\) 个三进制数,以 \(1\) 和 \(0\) 开头的随便取即可。
但是要保证不存在两个相同的串,且同一种数字出现种数为 \(N\) ,那么只需要每一个以 \(0\) 开头的数中每一位对应两个不同的数字即可,一个 \(x\) 对应 \(x + 1\) 和 \(x + 2\) ,在模三意义下。
int n, L, pw[20], stk[20];
inline void split(int k) {
int top = 0; memset(stk, 0, sizeof stk);
while(k) stk[++top] = k % 3, k /= 3;
}
int main() {
scanf("%d%d", &n, &L);
pw[0] = 1;
rep(i,1,L) pw[i] = pw[i - 1] * 3;
rep(i,0,n) {
split(i); putchar('2');
form(i,L - 1,1) putchar(stk[i] + '0'); puts("");
}
rep(i,0,n) {
split(i); putchar('1');
form(i,L - 1,1) putchar((stk[i] + 1) % 3 + '0'); puts("");
}
rep(i,0,n) {
split(i); putchar('0');
form(i,L - 1,1) putchar((stk[i] + 2) % 3 + '0'); puts("");
}
return 0;
}
C - Binary Strings
发现题目中按字典序排序的东西用 python
写出来的暴力就是下面这个东西。
def dfs(S) :
print(S + '\n')
dfs(S + '0')
dfs(S + '1')
dfs("1")
你直接画出来这个东西的 dfs
树,然后你发现这是一颗类似于 01
字典树的东西。
然后你想到把求解搞成和 01
字典树一样的询问。
若现在在第 \(d\) 层,那么向 0
边走会让 \(X\) 减一,向 1
走会让 \(X\) 减去 \(2 ^ {N - d}\),这个不难手推。
那对于二进制 \(X\),0
边相当于整体减一,1
边相当于把下标第 \(d\) 位变成 0
。
时间复杂度 \(\mathcal O (N)\) 。
int m, n, ept; char s[N], t[N];
inline void opt() {
int l = n;
do t[l--] ^= 1, ept ++ ; while(t[l + 1] & 1); ept -= 2;
}
void dfs(int d) {
if(!ept) return Wtn('\n');
if(t[d] & 1) Wtn('1'), ept -- , t[d] = 0, dfs(d + 1);
else Wtn('0'), opt(), dfs(d + 1);
}
inline void solve() {
Rdn(n), Rdn(s + 1);
m = strlen(s + 1);
forn(i,1,m) t[i + n - m] = s[i], ept += s[i] ^ '0';
Wtn('1'), opt(), dfs(1);
}
D - Sum of Min of Xor
先考虑如何求出 \(\sum_{1\le i < j \le N} A_i \oplus A_j\) 。
发现只有按每位考虑的方法。
发现要求的东西非常鬼畜,甚至没有关联,所以设 \(C_i = A_i \oplus B_i\) 。
这个时候题目就变成了求 \(\min\{A_i\oplus A_j, A_i\oplus A_j\oplus C_i \oplus C_j\}\) 。
然后就变成了要不要异或上 \(C_i\oplus C_j\) 的问题了。
还是按位考虑,从高位到低位贪心,如果有一位中,两种权值出现差异,那么这两个元素的结果必须选择其中一个在该位中更小的东西求出,也就是说,在这一位时,这两个元素的结果已经确定好了。
出现差异,相当于 \(C_i\) 和 \(C_j\) 在这一位不相等,考虑分治,每一次将每一位 \(C_i = 0\) 和 \(C_i = 1\) 的两个集合之间的贡献求出,然后往下递归即可。
i64 A[N], B[N], res, bit[2]; vector<int> r[2][2];
void dfs(vector<int> v, int d) {
int n = v.size();
if(!n) return ;
if(d == -1) {
rep(j,0,18) {
bit[0] = bit[1] = 0;
for(int i : v) {
res += bit[A[i] >> j & 1 ^ 1] << j;
bit[A[i] >> j & 1] ++ ;
}
}
return ;
}
vector<int> S[2];
rep(o,0,2) rep(oo,0,2) r[o][oo].clear();
for(int i : v) S[B[i] >> d & 1].push_back(i);
for(int i : v) r[B[i] >> d & 1][A[i] >> d & 1].push_back(i);
rep(o,0,2) rep(oo,0,2) {
if((o ^ oo) <= (o ^ oo ^ 1)) {
rep(j,0,18) {
bit[0] = bit[1] = 0;
for(int i : r[0][oo]) bit[(A[i] >> j) & 1] ++ ;
for(int i : r[1][o]) res += bit[((A[i] >> j) & 1) ^ 1] << j;
}
} else {
rep(j,0,18) {
bit[0] = bit[1] = 0;
for(int i : r[0][oo]) bit[((B[i] ^ A[i]) >> j) & 1] ++ ;
for(int i : r[1][o]) res += bit[(((B[i] ^ A[i]) >> j) & 1) ^ 1] << j;
}
}
}
dfs(S[0], d - 1), dfs(S[1], d - 1);
}
int n; vector<int> ini;
inline void solve() {
Rdn(n), ini.reserve(n);
forn(i,1,n) Rdn(A[i]);
forn(i,1,n) Rdn(B[i]), B[i] ^= A[i], ini.push_back(i);
r[0][0].reserve(n / 2);
r[0][1].reserve(n / 2);
r[1][1].reserve(n / 2);
r[1][0].reserve(n / 2);
dfs(ini, 17);
Wtn(res, '\n');
}