「Codeforces Round」#815 (Div 2)

CF1720.

A. Burenka Plays with Fractions

\[ \frac{a}{b} = \frac{c}{d} \]

\[ ad = cb \]

如果相等,答案为 \(0\)

如果某一方是 \(0\),答案为 \(1\)

如果两者有倍数关系,答案为 \(1\)

否则为 \(2\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int t, n;
void solve() {
	int a=read(), b=read(), c=read(), d=read();
	if(a*d==b*c) puts("0");
	else if(!a||!c) puts("1");
	else if(max(a*d,b*c)%min(a*d,b*c)==0) puts("1");
	else puts("2");
}
signed main() {
	t=read();
	while(t--) solve();
}

B. Interesting Sum

发现无论如何都能选择一个区间,满足区间内最大值是序列最大值或次大值,区间内最小值是序列最小值或次小值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=1e5+5;
int t, n, a[N];
void solve() {
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	sort(a+1,a+n+1);
	int ans=a[n]-a[1]+a[n-1]-a[2];
	printf("%lld\n",ans);
}
signed main() {
	t=read();
	while(t--) solve();
}

C. Corners

发现第一次操作后,每次操作一定都能只消耗一个白点。

如果存在两个 \(0\) 能被同时操作,那么第 \(1\) 次操作就会消耗 \(1\) 个白点,否则,如果存在 \(0\),就会消耗 \(2\) 个,否则就会消耗 \(3\) 个。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,j,k) for(int i=j;i<=k;++i)
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
int t, n, m, a[505][505];
void solve() {
	n=read(), m=read();
	rep(i,1,n) rep(j,1,m) scanf("%1d",&a[i][j]);
	int cnt1=0, dlt=1;
	rep(i,1,n) rep(j,1,m) cnt1+=a[i][j];
	rep(i,1,n) rep(j,1,m) {
		if(!a[i][j]) {
			if(i>1&&!a[i-1][j]) dlt=0;
			else if(i<n&&!a[i+1][j]) dlt=0;
			else if(j>1&&!a[i][j-1]) dlt=0;
			else if(j<m&&!a[i][j+1]) dlt=0;
			else {
				if(i>1&&j>1&&!a[i-1][j-1]) dlt=0;
				else if(i>1&&j<m&&!a[i-1][j+1]) dlt=0;
				else if(i<n&&j>1&&!a[i+1][j-1]) dlt=0;
				else if(i<n&&j<m&&!a[i+1][j+1]) dlt=0;
			}
			if(!dlt) break;
		}
	}
	if(cnt1==n*m) dlt=2;
	printf("%lld\n",cnt1-dlt);
}
signed main() {
	t=read();
	while(t--) solve();
}

D1. Xor-Subsequence (easy version)

这题被 Hack 了,原因是使用memset()

注意到 \(a_i \in [0,200]\)

\(f(i)\) 为以 \(i\) 结尾的 beautiful subsequence 的最大长度,以下下标均从 \(0\) 开始。

初始值 \(f(i)=1\)\[ f(i) = \max_{j \in [0,i-1]} \{ f(j) + 1 \} \] 其中 \(a_j \oplus i < a_i \oplus j\)

这样的复杂度是 \(O(n^2)\) 的。

由于 \(a_i\) 不超过 \(2^8\),所以 \(j \in [\max(0,i-2^8),i-1]\)

可过。

答案 \(\max_{i=0}^{n-1} f_i\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=3e5+5;
int t, n, a[N], f[N];
void solve() {
	n=read();
	for(int i=0;i<n;++i) f[i]=0;
	int ans=0;
	for(int i=0;i<n;++i) a[i]=read();
	for(int i=0;i<n;++i) {
		f[i]=1;
		for(int j=max(0ll,i-256);j<i;++j) {
			if((a[j]^i)<(a[i]^j)) f[i]=max(f[i],f[j]+1);
		}
		ans=max(ans,f[i]);
	}
	printf("%lld\n",ans);
}
signed main() {
	t=read();
	while(t--) solve();
}

D2. Xor-Subsequence (hard version)

\(a_i \in [0,10^9]\),上面的做法失效了。考虑优化这个 DP。

如果 \(a_j \oplus i < a_i \oplus j\),那么一定存在一个 \(k\),满足前 \(k\) 位中,\(a_j \oplus i = a_i \oplus j\)\(k+1\) 位上,前者为 \(0\),后者为 \(1\)

\(w(k,x)\)\(x\) 二进制中第 \(k\) 位的值。

不知道为什么下面部分字体出现了问题,但是不影响观看。

如果 \(w(1 \sim k, a_j \oplus i) = w(1 \sim k,a_i \oplus j)\),那么两边同时异或上 \(i \oplus j\),即 \(w(1 \sim k,a_j \oplus j) = w(1 \sim k,a_i \oplus i)\)。这个转化,直接将 \(O(n^2)\) 级别的数对变成 \(O(n)\) 级别的了。

\(k+1\) 位上,有 \(a_j \oplus j < a_i \oplus i\),这表明 \(w(k+1,a_i \oplus i) \oplus 1 = w(k+1,a_j \oplus j)\),这是第一个要点。

由于 \(w(k+1,a_j \oplus i) < w(k+1,a_i \oplus j)\),所以 \(w(k+1,a_i \oplus j) = 1\),即 \(w(k+1,a_i) \neq w(k+1,j)\),这是第二个要点。

建立一颗 0-1 Trie 来维护这个东西,插入 \(a_i \oplus i\)。对于每个 \(f(i)\),通过上述方法找到满足条件的最大的 \(f(j)\)

\(g(x,k=0/1)\) 为字典树上经过 \(x\) 节点,且这一位为 \(0/1\) 的最大的 \(f(j)\)。上文两个要点已经说明的求的满足条件的 \(j\) 的方法,即要过 \(w(k+1,a_i \oplus i) \oplus 1\) 这个节点,且第 \(k+1\) 位与 \(w(k+1,a_i)\) 不同。

复杂度 \(O(n \log_2 n)\)

#include<bits/stdc++.h>
using namespace std;
#define int long long
int read() {
	int a=0, f=1; char c=getchar();
	while(!isdigit(c)) {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(isdigit(c)) a=a*10+c-'0', c=getchar();
	return a*f;
}
const int N=3e5+5;
int t, n, tot, a[N], f[N], trie[30*N][2], g[30*N][2];
void insert(int s,int id) {
	int x=0;
	for(int i=30;~i;--i) {
		int a=(s>>i)&1;
		if(!trie[x][a]) trie[x][a]=++tot;
		x=trie[x][a];
		g[x][(id>>i)&1]=max(g[x][(id>>i)&1],f[id]);
        // id为下标
	}
}
int query(int s,int k) {
	int ans=0, x=0;
	for(int i=30;~i;--i) {
		int a=(s>>i)&1, y=trie[x][a^1];
        // y=a[i]^i^1的这一位所在的节点
		ans=max(ans,g[y][(k>>i)&1^1]+1);
        // k为a[i],(k>>i)&1^1等于在这一位上,y与k不同
		if(!trie[x][a]) break;
        // 没有后续的节点了,结束
		x=trie[x][a];
	}
	return ans;
}
void solve() {
	n=read();
	for(int i=0;i<n;++i) a[i]=read(), f[i]=1;
	for(int i=0;i<=tot;++i) trie[i][0]=trie[i][1]=g[i][0]=g[i][1]=0;
	tot=0;
	for(int i=0;i<n;++i) {
		f[i]=query(a[i]^i,a[i]);
		insert(a[i]^i,i);
        // 注意插入和查询的参数不同
	}
	int ans=0;
	for(int i=0;i<n;++i) ans=max(ans,f[i]);
	printf("%lld\n",ans);
}
signed main() {
	t=read();
	while(t--) solve();
}

「Codeforces Round」#815 (Div 2)
https://yozora0908.github.io/2022/cf1720-solution/
作者
yozora0908
发布于
2022年8月19日
许可协议