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');
}

E - Priority Queue

F - ±AB

posted @ 2021-09-30 14:53  AxDea  阅读(164)  评论(0编辑  收藏  举报