AtCoder Regular Contest 127 題解


sb atcoder 提前比賽時間/fn/fn/fn……sb atcoder 還我 rating/zk/zk/zk

A

簽到題,枚舉位數 \(+\) 前導 \(1\) 個數然后隨便算算貢獻即可,時間復雜度 \(\log^2_{10}(n)\)

B

u1s1 這個 B 感覺比一般的 B 難不少啊……

考慮貪心。首先第一位我們隨便欽定 \(n\)\(0\)\(n\)\(1\)\(n\)\(2\),那么后面的位我們肯定會貪心地讓第一位填 \(2\) 的都填 \(0\),第一位填 \(0/1\) 的自然不用管它們因為我們后面自然有辦法為它們填數,但是這樣會導致字符串出現重復的情況,怎么解決呢?如果我們發現目前不能區分出來的字符串個數 \(>3^{l-i}\),其中 \(i\) 表示當前填到了第幾位,那么我們就貪心地依次填上 \(0000...0,000...1,\cdots,1111...11\),然后把最后的零頭放到那“不能被區分出來的字符串集合”中即可。

可能講得有點玄乎,看代碼可能會比較好理解。

const int MAXN=1.5e5;
int n,l,pw3[18];
string s[MAXN+5];
int main(){
	scanf("%d%d",&n,&l);
	for(int i=(pw3[0]=1);i<=l;i++) pw3[i]=pw3[i-1]*3;
	vector<int> id;
	for(int i=1;i<=n;i++) id.pb(i);int cur=n;
	for(int i=1;i<=n;i++) s[i].pb('0'),s[i+n].pb('1'),s[i+n+n].pb('2');
	for(int _=2;_<=l;_++){
		cur=id.size();
		if(cur<=pw3[l-_]){
			for(int x:id) s[x].pb('2'),s[x+n].pb('1'),s[x+n+n].pb('0');
		} else if(cur<=pw3[l-_]*2) {
			for(int i=0;i<pw3[l-_];i++){
				int x=id[i];
//				printf("%d\n",x);
				s[x].pb('2');s[x+n].pb('1');s[x+n+n].pb('0');
				for(int j=0;j<l-_;j++){
					s[x].pb('0'+(i/pw3[j]%3));
					s[x+n].pb('0'+(i/pw3[j]%3));
					s[x+n+n].pb('0'+(i/pw3[j]%3));
				}
			} vector<int> nw;
			for(int i=pw3[l-_];i<id.size();i++){
				int x=id[i];
//				printf("%d\n",x);
				s[x].pb('0');s[x+n].pb('2');s[x+n+n].pb('1');
				nw.pb(x);
			} id=nw;
		} else {
			for(int i=0;i<pw3[l-_];i++){
				int x=id[i];
				s[x].pb('2');s[x+n].pb('1');s[x+n+n].pb('0');
				for(int j=0;j<l-_;j++){
					s[x].pb('0'+(i/pw3[j]%3));
					s[x+n].pb('0'+(i/pw3[j]%3));
					s[x+n+n].pb('0'+(i/pw3[j]%3));
				}
			}
			for(int i=pw3[l-_];i<pw3[l-_]*2;i++){
				int x=id[i];
				s[x].pb('0');s[x+n].pb('2');s[x+n+n].pb('1');
				for(int j=0;j<l-_;j++){
					s[x].pb('0'+(i/pw3[j]%3));
					s[x+n].pb('0'+(i/pw3[j]%3));
					s[x+n+n].pb('0'+(i/pw3[j]%3));
				}
			} vector<int> nw;
			for(int i=pw3[l-_]*2;i<id.size();i++){
				int x=id[i];
				s[x].pb('1');s[x+n].pb('0');s[x+n+n].pb('2');
				nw.pb(x);
			} id=nw;
		}
	}
	for(int i=1;i<=n*3;i++) cout<<s[i]<<endl;
	return 0;
}

C

請問您見過幾個 arc C 被評到 2000+

首先考慮一個非常朴素的做法,我們先事先將 \(X\)\(1\),然后每次從高位到低位一位一位考慮,如果當前 \(X\) 等於零那就直接 break,否則如果當前 \(X\le 2^{n-l}\),其中 \(l\) 為當前考慮的位,那么我們就在這一位上填 \(0\) 並將 \(X\) 減一,否則我們就在這一位上填 \(1\) 並將 \(X\) 減去 \(2^{n-l}\)

然后就是我降智的地方了。由於這題 \(X\) 很大需要高精度,因此我以為直接減 \(1\) 復雜度會退化到平方,於是想着打各種標記之類的奇怪玩意兒,WA 了好幾發,事實上直接減 \(1\) 復雜度是正確的,感性理解一下不難發現當你進行一次減 \(1\) 之后退了很多很多位,那么在接下來很長一段時間內就不可能再退這么多位,如果理性證明地大概就 \(\sum\limits_{i=0}^{\log_2(n)}\lfloor\dfrac{n}{2^i}\rfloor=\mathcal O(n)\),因此總時間復雜度也是 \(\mathcal O(n)\)

D

考慮 \(c_i=a_i\oplus b_i\),那么不難發現對於一組 \(i,j\)\(a_i\oplus a_j\)\(b_i\oplus b_j\) 從高到低第一個值不同的位就是 \(c_i\oplus c_j\) 二進制下的 significant bit,下文中稱其為第 \(j\) 位,因此 \(\min(a_i\oplus a_j,b_i\oplus b_j)\) 取到 \(a_i\oplus a_j\) 當且僅當 \(a_i\oplus a_j\)\(d\) 位為 \(1\)

我們再考慮這樣一個問題,給定序列 \(a\) 求它們兩兩異或和的和,01-trie 是一種方法,事實上我賽時一開始也考慮了這個做法,但是有一種更簡單的做法是你考慮每一位的貢獻,我們對於每一位記錄一下有多少個數這一位為 \(0\),有多少個數這一位為 \(1\),這樣可以 \(\mathcal O(1)\) 計算每一位的貢獻。

接下來考慮原問題。我們將所有 \(c_i\) 插入 01-trie,然后對於 \(c_i\) 每一個為 \(1\) 的位 \(d\),我們在其對應的 01-trie 的節點上開 4 個長度為 \(\log(n)\) 的數組,下標為 \(j\) 的位置分別維護這樣幾個信息:對於經過這個節點且 \(c_i\)\(1\)\(i\),有多少個:

  • \(a_i\)\(d\) 位為 \(0\)\(i\),滿足 \(a_i\) 的第 \(j\) 位為 0/1
  • \(a_i\)\(d\) 位為 \(0\)\(i\),滿足 \(b_i\) 的第 \(j\) 位為 0/1
  • \(a_i\)\(d\) 位為 \(1\)\(i\),滿足 \(a_i\) 的第 \(j\) 位為 0/1
  • \(a_i\)\(d\) 位為 \(1\)\(i\),滿足 \(b_i\) 的第 \(j\) 位為 0/1

這樣在遍歷 01-trie 計算貢獻的過程中,我們考慮計算一個 \(i\) 對答案的貢獻時,可以考慮 \(c_i\) 所有為 \(0\) 的位,然后通過預處理的信息在單次 \(\mathcal O(\log n)\) 的時間內計算貢獻。

記得加上 \(c_i=c_j\) 的貢獻,這一部分的計算是 trivial 的,直接對每一種 \(c_i\) 重復一遍上面弱化版的過程即可。

時間復雜度 \(n\log^2n\)

const int MAXN=262144;
const int LOG_N=18;
const int MAXP=2097152;
int n,A[MAXN+5],B[MAXN+5],C[MAXN+5];
int ch[MAXP+5][2],cnt[4][MAXP+5][LOG_N+2][2],rt=0,ncnt=0;
vector<int> pc[MAXN+5];
ll res=0;
void insert(int &k,int v,int dep,int a,int b){
	if(!k) k=++ncnt;if(!~dep) return;
	insert(ch[k][v>>dep&1],v,dep-1,a,b);
	if(v>>dep&1){
		if(a>>dep&1){
			for(int j=0;j<LOG_N;j++) cnt[0][ch[k][v>>dep&1]][j][a>>j&1]++;
			for(int j=0;j<LOG_N;j++) cnt[1][ch[k][v>>dep&1]][j][b>>j&1]++;
		} else {
			for(int j=0;j<LOG_N;j++) cnt[2][ch[k][v>>dep&1]][j][a>>j&1]++;
			for(int j=0;j<LOG_N;j++) cnt[3][ch[k][v>>dep&1]][j][b>>j&1]++;
		}
	}
}
void query(int k,int v,int dep,int a,int b){
	if(!k||!~dep) return;
	query(ch[k][v>>dep&1],v,dep-1,a,b);
	if(~v>>dep&1){
		int c=ch[k][~v>>dep&1];
		if(!c) return;
//		printf("%d %d %d %d %d\n",v,dep,c,a,b);
		if(a>>dep&1){
			for(int j=0;j<LOG_N;j++) res+=1ll*cnt[0][c][j][~a>>j&1]*(1<<j);
			for(int j=0;j<LOG_N;j++) res+=1ll*cnt[3][c][j][~b>>j&1]*(1<<j);
		} else {
			for(int j=0;j<LOG_N;j++) res+=1ll*cnt[1][c][j][~b>>j&1]*(1<<j);
			for(int j=0;j<LOG_N;j++) res+=1ll*cnt[2][c][j][~a>>j&1]*(1<<j);
		}
	}
}
int cnt0[MAXN+5][LOG_N+2][2];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&A[i]);
	for(int i=1;i<=n;i++) scanf("%d",&B[i]),C[i]=A[i]^B[i],pc[C[i]].pb(i);
	for(int i=1;i<=n;i++) insert(rt,C[i],LOG_N-1,A[i],B[i]);
	for(int i=1;i<=n;i++) for(int j=0;j<LOG_N;j++)
		cnt0[C[i]][j][A[i]>>j&1]++;
//	for(int i=1;i<=n;i++) printf("%d\n",c[i]);
	for(int i=0;i<MAXN;i++){
		ll s=0;
		for(int id:pc[i]) for(int j=0;j<LOG_N;j++)
			s+=1ll*cnt0[i][j][~A[id]>>j&1]*(1<<j);
		s>>=1;res+=s;
	}
	for(int i=1;i<=n;i++) query(rt,C[i],LOG_N-1,A[i],B[i]);
	printf("%lld\n",res);
	return 0;
}

E

並沒有自己想出來,搬運官方題解 ing

首先考慮如果我們想要盡量讓大的數都在最終的 \(s\) 中那該怎么選擇每次插入的數。顯然按 \(1,2,3,\cdots,A\) 的順序插入肯定是最優的。因此我們假設 \(b_1,b_2,\cdots,b_{A-B}\) 為按 \(1,2,3,\cdots,A\) 順序插入后剩余的數組成的集合,其中 \(b_i<b_{i+1}\)

我們再假設 \(c_1,c_2,\cdots,c_{A-B}(c_i<c_{i+1})\) 為按某種順序插入后剩余的數組成的集合,那么我們不妨猜測 \(c_i\le b_i(i\in[1,A-B])\)。我們假設 \(p_1,p_2,\cdots,p_{B}\) 為按 \(1,2,3,\cdots,A\) 中不在剩余集合中的數組成的集合,\(q_1,q_2,\cdots,q_B\) 為按 \(c_1,c_2,\cdots,c_{A-B}\) 對應的順序插入后不在剩余集合中的數組成的集合,其中 \(p_i<p_{i+1},q_i<q_{i+1}\),然后對於第 \(i\) 次插入考慮這樣構造:

  • 如果 \(\exists j,s.t.b_j=i\),那么我們此次插入中插入 \(c_j\),其中 \(b_j=i\)
  • 否則必然 \(\exists j,s.t.p_j=i\),我們就插入 \(q_j\)

不難證明最后得到的數就是 \(c_1,c_2,\cdots,c_{A-B}\)。因此 \(c_1,c_2,\cdots,c_{A-B}\) 可以作為最終集合存在的充要條件是 \(\forall i\in[1,A-B],c_i\le b_i\)。到這里事情就變得很 trivial 了。\(dp_{i,j}\) 表示考慮了前 \(i\) 個數,\(c_i=j\) 的合法的 \(c\) 的個數,前綴和優化 DP 即可,時間復雜度 \(A^2\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM