「Codeforces Round」#816 (Div 2)

CF1715.

A. Crossmarket

钦定 \(n \ge m\)

不难发现,最优操作方式一定是一个先走完 \((n-1) + (m-1)\) 的路程,另一个只需要走 \((m-1)\) 的路程加上 \(1\) 的传送即可。

#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, m;
void solve() {
	n=read(), m=read();
	if(n==1&&m==1) { puts("0"); return; }
	if(n<m) swap(n,m);
	printf("%lld\n",(n-1)+(m-1)+(m-1)+1);
}
signed main() {
	t=read();
	while(t--) solve();
}

B. Beautiful Array

既然是构造题,那么我们可以乱搞。

考虑如果 \(a_i < k\),那么不会对序列的 beauty 产生贡献。

由于存在向下取整,所以序列的和最小是 \(k \cdot b\),如果 \(k \cdot b > s\),那么无解。

\(a_1\)\(\min(k \cdot (b+1)-1,s)\),如果 \(s - a_1\) 之后还有剩余,那么就贪心地在后面放上不超过 \(k\) 的数字就行了。

如果没有位置可以放的时候,\(s\) 仍然大于 \(0\),那么无解。

#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, k, b, s, a[N];
void solve() {
	n=read(), k=read(), b=read(), s=read();
	if(k*b>s) { puts("-1"); return; }
	for(int i=1;i<=n;++i) a[i]=0;
	int t=k*(b+1)-1;
	if(t>s) t-=t-s;
	a[1]=t;
	s-=t;
	for(int i=2;i<=n;++i) {
		if(!s) break;
		if(s>=k-1) a[i]=k-1, s-=(k-1);
		else if(s>0) a[i]=s, s=0;
	}
	if(s!=0) { puts("-1"); return; }
	for(int i=1;i<=n;++i) printf("%lld%c",a[i]," \n"[i==n]);
}
signed main() {
	t=read();
	while(t--) solve();
}

C. Monoblock

先求出原序列的值,考虑每个数的贡献。

不太好想,举个例子。假设 \(1,2\) 的贡献已经计算完毕,现在要在后面加入一个 \(3\)。不难发现,所有以 \(3\) 结尾的前缀,\(1,2,3\)\(2,3\)\(3\) 中都存在 \(3\)\(1\) 个贡献,所以这部分的贡献是 \(3\)。而由于这 \(3\) 段都是新加入的,所以也要计算 \(1\)\(2\) 的贡献。不难发现这就是一个相同的问题,以 \(2\) 结尾的前缀的个数加上以 \(1\) 结尾的前缀的个数。

而如果加入的是 \(2\) 呢?则只会让以 \(2\) 结尾的前缀个数增加 \(1\),其他是相同的。

形式化地,设 \(pre\) 为所有 \(k \in [1,i-1]\),以 \(k\) 结尾的前缀的数量,\(ans\)\([1,i-1]\) 的贡献。加入 \(a_i\)

假设 \(a_1,\ldots a_{i-1}\) 的贡献已经计算完毕,那么插入 \(a_i\) 时,如果 \(a_{i} \neq a_{i-1}\),那么令 \(pre + i\)\(ans + pre\)。否则令 \(pre+1\)\(ans + pre\)

对于修改操作,如果 \(a_i \neq a_{i-1}\)\(x = a_{i-1}\),那么左端点在 \([1,i-1]\),右端点在 \([i,n]\) 中的区间,其贡献都会减少 \(1\),所以要减去 \((i-1) \cdot (n-i+1)\)。同理,如果 \(a_i \neq a_{i+1}\)\(x = a_{i+1}\),那么就要减去 \(i \cdot (n-i)\)

反过来,如果 \(a_i = a_{i-1}\)\(x \neq a_{i-1}\) 或者 \(a_i = a_{i+1}\)\(x \neq a_{i+1}\),那么就要加上对应的值。

#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, m, cnt, a[N], b[N];
void solve() {
	n=read(), m=read();
	int ans=1, pre=1;
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=2;i<=n;++i) {
		if(a[i]!=a[i-1]) pre+=i, ans+=pre;
		else ++pre, ans+=pre;
	}
	while(m--) {
		int i=read(), x=read();
		if(a[i]==x) { printf("%lld\n",ans); continue; }
		if(x==a[i-1]&&a[i]!=a[i-1]) ans-=(i-1)*(n-i+1);
		else if(x!=a[i-1]&&a[i]==a[i-1]) ans+=(i-1)*(n-i+1);
		if(x==a[i+1]&&a[i]!=a[i+1]) ans-=i*(n-i);
		else if(x!=a[i+1]&&a[i]==a[i+1]) ans+=i*(n-i);
		a[i]=x;
		printf("%lld\n",ans);
	}
}
signed main() {
	solve();
}

D. 2+ doors

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

如果 \(a_i | a_j = x\),那么如果 \(g(k,x)=0\),那么一定有 \(g(k,a_i) = g(k,a_j) = 0\)

考虑反着构造,先将每个 \(a_i\) 赋值为 \(2^{30} - 1\)

对于一组 \((i,j,x)\),如果 \(i \neq j\),那么就执行上述操作。

执行完之后,\(a_i | a_j\) 必然等于 \(k\),下面着手减小它的字典序。

对于一个 \(i\),枚举其为 \(1\) 的位 \(k\),找到所有与 \(i\) 有关的 \((j,x)\),如果上满足存在一组 \((j,x)\),满足 \(g(k,a_j) = 1\)\(g(k,x) = 0\),那么就将 \(g(k,a_1)\) 改为 \(0\)

这样做会导致其他的 \((j,x)\) 不满足条件,但为什么是最优解呢?因为数据保证有解,由于枚举 \(i\) 是从前往后,所以减小靠前的数要优于减小靠后的数,且可以通过修改 \(a_j\) 的方式重新满足条件。

#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 n, q, a[N];
bool v[N];
vector<pair<int,int> > w[N];
signed main() {
	n=read(), q=read();
	for(int i=1;i<=n;++i) a[i]=(1<<30)-1;
	while(q--) {
		int i=read(), j=read(), x=read();
		if(i==j) {
			a[i]=x, v[i]=1;
			continue;
		}
		for(int k=29;~k;--k) {
			if(x>>k&1) continue;
			if(a[i]>>k&1) a[i]^=1<<k;
			if(a[j]>>k&1) a[j]^=1<<k;
		}
		w[i].push_back({j,x});
		w[j].push_back({i,x});
	}
	for(int i=1;i<=n;++i) if(!v[i]) {
		for(int j=29;~j;--j) {
			if(!(a[i]>>j&1)) continue;
			bool fg=1;
			for(auto p:w[i]) {
				#define x first
				#define y second
				if((p.y>>j&1)&&!(a[p.x]>>j&1)) { fg=0; break; }
			}
			if(fg) a[i]^=1<<j;
		}
	}
	for(int i=1;i<=n;++i) printf("%lld%c",a[i]," \n"[i==n]);
}

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