ARC136

A 把所有 A 换 BB,再贪心换回 A

#include <cstdio>
using namespace std;
char s[200003];
int n;
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	bool fl=0;
	for(int i=1;i<=n;++i){
		if(s[i]=='B'){
			if(fl) putchar('A'),fl=0;
			else fl=1;
		}
		else if(s[i]=='A') putchar('A');
		else{
			if(fl) putchar('B'),fl=0;
			putchar(s[i]);
		}
	}
	if(fl) putchar('B');
	putchar('\n');
	return 0;
}

B 考虑对于排列怎么做:一次操作不改变逆序对奇偶性,可以证明这个条件充分。可以考虑将每一个 \(i\in [1,n-2]\) 放回原位,如果位置奇偶性不同,那么就让 i 的后两位先往前跳一次,发现只有最后两个数无法调整,而这两个数的位置关系由逆序对奇偶性可以决定

现在可能有重复数字,那么钦定这两个数字为排列最大和次大,那么这两个数一定可以被丢后面,一定可以满足条件

那么判掉重复数字再跑逆序对即可

#include <cstdio>
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
const int _=5003;
int a[_],b[_],c[_],n;
void upd(int x){for(int i=x;i<=5000;i+=(i&-i))++c[i];}
int qry(int x){int r=0;for(int i=x;i;i-=(i&-i))r+=c[i];return r;}
int cnta[_],cntb[_];
int main(){
	n=read();
	for(int i=1;i<=n;++i) ++cnta[a[i]=read()];
	for(int i=1;i<=n;++i) ++cntb[b[i]=read()];
	for(int i=1;i<=5000;++i)
		if(cnta[i]^cntb[i]){
			puts("No");
			return 0;
		}
	for(int i=1;i<=5000;++i)
		if(cnta[i]>1||cntb[i]>1){
			puts("Yes");
			return 0;
		}
	bool res=0;
	for(int i=1;i<=5000;++i) c[i]=0;
	for(int i=1;i<=n;++i){
		upd(a[i]);
		res^=(i-qry(a[i]))&1;
	}
	for(int i=1;i<=5000;++i) c[i]=0;
	for(int i=1;i<=n;++i){
		upd(b[i]);
		res^=(i-qry(b[i]))&1;
	}
	if(res) puts("No");
	else puts("Yes");
	return 0;
}

C 观察发现可以在最小值前面一个位置破环成链跑序列上的原问题,可以笛卡尔树解决,但是有一个操作就不能实现,类似于操作 \(\{1,2,3,4,5\},\{5,6,1,2\}\) 这两个序列

发现这样的操作等价于 \(\{1,2,3,4,5,6\},\{1,2\},\{5\}\) ,发现其作用效果是对整个环操作一次,再把两个不相交的区间操作合并

相当于我们有 \(\min_{i=1}^n a_i\) 次合并不相交区间的机会,这个在处理笛卡尔树时顺便减掉

#include <cstdio>
#include <algorithm>
using namespace std;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
typedef long long ll;
const int _=400003;
int n,a[_],mn; ll res;
int cmp(int i,int j){return a[i]<a[j]?i:j;}
int f[20][_],lg[_];
int qry(int l,int r){
	int k=lg[r-l+1]-1;
	return cmp(f[k][l],f[k][r-(1<<k)+1]);
}
ll solve(int l,int r,ll cur){
	if(l>r) return 0;
	int p=qry(l,r);
	ll lc=solve(l,p-1,a[p]),rc=solve(p+1,r,a[p]);
	ll tt=min({lc,rc,res});
	res-=tt;
	return lc+rc+a[p]-cur-tt;
}
int main(){
	n=read();mn=0x7fffffff;
	for(int i=1;i<=n;++i) a[i]=read(),mn=min(mn,a[i]);
	for(int i=n+1;i<=n+n;++i) a[i]=a[i-n];
	for(int i=1;i<=n+n;++i) f[0][i]=i,lg[i]=lg[i>>1]+1;
	for(int t=1;t<20;++t)
		for(int i=1;i+(1<<t)-1<=n+n;++i)
			f[t][i]=cmp(f[t-1][i],f[t-1][i+(1<<(t-1))]);
	int l=1,r=n;res=mn;
	while(a[r]^mn) ++r;
	while(a[l]^mn) ++l;
	printf("%lld\n",solve(l,r,0));
	return 0;
}

赛时只做到这……太慢了

D 能与数 \(a\) 相加还不进位的数就是满足十进制每一位对应小于 \(999999-a\) 的十进制每一位,这是一个高维前缀和的形式,直接做

#include <cstdio>
using namespace std;
const int _=1000003;
int cnt[_],a[_],n;
int read(){
	char c=getchar();int x=0;
	while(c<48||c>57) c=getchar();
	do x=(x<<1)+(x<<3)+(c^48),c=getchar();
	while(c>=48&&c<=57);
	return x;
}
int main(){
	n=read();
	for(int i=1;i<=n;++i) ++cnt[a[i]=read()];
	for(int i=1;i<1000000;++i)
		if(i%10) cnt[i]+=cnt[i-1];
	for(int i=10;i<1000000;++i)
		if(i/10%10) cnt[i]+=cnt[i-10];
	for(int i=100;i<1000000;++i)
		if(i/100%10) cnt[i]+=cnt[i-100];
	for(int i=1000;i<1000000;++i)
		if(i/1000%10) cnt[i]+=cnt[i-1000];
	for(int i=10000;i<1000000;++i)
		if(i/10000%10) cnt[i]+=cnt[i-10000];
	for(int i=100000;i<1000000;++i)
		if(i/100000%10) cnt[i]+=cnt[i-100000];
	long long res=0;
	for(int i=1;i<=n;++i){
		res+=cnt[999999-a[i]];
		while(a[i]){
			if(a[i]%10>4) {++res;break;}
			a[i]/=10;
		}
		--res;
	}
	printf("%lld\n",res/2);
	return 0;
}

感觉这道题比 C 简单很多(主要是太板)

E 妙题,看了题解才会

进行几个关键的观察:偶数之间全部可达

考虑怎么处理奇数,发现对于一个奇数 \(x\) ,设 \(v_x\) 表示最小质因子,那么第一个能到他的节点是 \(x-v_x\) ,第一个他能到的节点为 \(x+v_x\),这两个数都是偶数

也就是说设一个数往前第一次能到的偶数为 \(L_i\)(如果是偶数就是本身) ,往后第一次能到的奇数为 \(R_i\)(如果是偶数就是本身)

那么只要 \(R_x\leq L_y(x<y)\)\(x\) 就能到 \(y\)

现在让我们选一个全部相交的集合,那么这个集合至少交于一个点,用区间覆盖差分统计答案即可

#include <cstdio>
using namespace std;
#define gec (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin)),p1==p2?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
int read(){
	char c=gec;int x=0;
	while(c<48||c>57) c=gec;
	do x=(x<<1)+(x<<3)+(c^48),c=gec;
	while(c>=48&&c<=57);
	return x;
}
const int N=1000003;
const int K=78500;
int n,a[N],v[N];
int pr[K],m;
long long f[N*2],res;
int main(){
	n=read();
	for(int i=1;i<=n;++i) a[i]=read();
	for(int i=2;i<=n;++i){
		if(!v[i]) v[i]=pr[++m]=i;
		for(int j=1;j<=m&&pr[j]*i<=n;++j){
			v[i*pr[j]]=pr[j];
			if(i%pr[j]==0) break;
		}
	}
	res=f[1]=a[1];
	for(int i=2;i<=n;++i)
		if(i&1){
			f[i-v[i]+1]+=a[i];
			f[i+v[i]]-=a[i];
		}
		else{
			f[i]+=a[i];
			f[i+1]-=a[i];
		}
	for(int i=1;i<=2*n;++i){
		f[i]+=f[i-1];
		if(f[i]>res) res=f[i];
	}
	printf("%lld\n",res);
	return 0;
}
posted @ 2022-02-28 10:06  yyyyxh  阅读(49)  评论(0编辑  收藏  举报