Codeforces Round #641 (Div. 2) A-E(1350,1349)

这场好毒瘤,C不特判+不知道哪里写炸导致一直过不去,赛后换了写法才过/kk
然后下分到1600,结果还是不能记分打div3/kk

CF1350 A. Orac and Factors

https://codeforces.com/problemset/problem/1350/A
给出 (nle 10^6,kle 10^9),让 (n) 每次加上自己的最小因数,这样加 (k)

  • (n) 是偶数,那么每次加的最小因数都是 (2),答案 (n+2k)
  • 奇数,它的所有因数肯定都是奇数,那么先枚举出它的最小因数 (x),加上 (x) 就变成了偶数,按照偶数的处理方法再加 (k-1) 次就行,答案 (n+x+2(k-1))
int main(){int T=read();while(T--){
	LL n=read(),k=read();
	if(n&1){
		for(reg int i=3;;i++)if(!(n%i)){
			n+=i;break;
		}
		k--;
	}
	std::printf("%lld
",n+2*k);
}
	return 0;
}

CF1350 B. Orac and Models

https://codeforces.com/problemset/problem/1350/B
给出一列数,要求选其中一些数,符合:

  • 排列顺序按照在原数列中的顺序排
  • 以上面的排列方式,数大小递增
  • 以上面的排列方式,相邻两数在原数列中的下标,前一个数是后一个数的因数

求最多能选几个数

没想到div2B就会出简单的dp
(f_i) 为以 (i) 开头,最多能选几个
那么倒着枚举,对于每个 (i)(f_i=max(f_j mid i ext{ 是 }j ext{ 的因数,且} a_j>a_i)+1)
(a) 就是原数列,转移式子比较显然

int a[100006],f[100006];
int main(){int T=read();while(T--){
	int n=read();
	for(reg int i=1;i<=n;i++) a[i]=read();
	int ans=0;
	for(reg int i=n;i;i--){
		f[i]=0;
		for(reg int j=i+i;j<=n;j+=i)
			if(a[j]>a[i]) f[i]=std::max(f[i],f[j]);
		f[i]++;
		ans=std::max(ans,f[i]);
	}
	std::printf("%d
",ans);
}
	return 0;
}

CF1349 A. Orac and LCM

https://codeforces.com/problemset/problem/1349/A
给出数列 (a),求 (gcd({operatorname{lcm}({a_i,a_j}) | i<j}))
就是对于每一对 (a_i,a_j),让它们求一个 (operatorname{lcm}),放到一个新数列里,然后再求这个新数列的 (gcd)

把每个数分解一下,设:

[a_i=prod_{p_jin prime} p_j^{m_{ij}} ]

则:

[operatorname{lcm}(a_i,a_j)=prod_{p_kin prime} p_k^{t_k},t_k=max(m_{ik},m_{jk}) ]

[gcd(a_i,a_j)=prod_{p_kin prime} p_k^{t_k},t_k=min(m_{ik},m_{jk}) ]

说人话,就是取 (operatorname{lcm}) 的时候就是取质因数质数的较大值,(gcd) 就是取较小值

那么要求的答案:

[prod_{p_kin prime} p_k^{t_k},t_k=min({max(m_{ik},m_{jk}) | i<j}) ]

就是每个指数,都是取每两对数对应的指数,取 (max),再在这些 (max) 里取一个 (min)
那么,对于每一个质因数,我们找出数列中每个数它的指数的最小值和次小值,让答案乘上它的次小值次幂就行了
还要用 (vis) 数组标记一下一个质因数在数列的数中出现了几次,可以看注释

LL a[100005];
int min2[200006],min[200006],vis[200006];
int prime[100005],notprime[200006];
inline void get_prime(int n){
	for(reg int i=2;i<=n;i++){
		if(notprime[i]) continue;
		prime[++prime[0]]=i;
		for(reg int j=i+i;j<=n;j+=i) notprime[j]=1;
	}
}
int main(){
//		std::freopen("tmp.txt","r",stdin);
	int n=read();
	for(reg int i=1;i<=n;i++) a[i]=read();
	if(n==2) return std::printf("%lld",a[1]/std::__gcd(a[1],a[2])*a[2]),0;
	std::memset(min,0x7f,sizeof min);std::memset(min2,0x7f,sizeof min2);
	LL ans=1;
	get_prime(200000);
	for(reg int i=1;i<=n;i++){
		int num,sqrt=std::sqrt(a[i]);
		for(reg int j=1;j<=prime[0];j++){
			if(a[i]<prime[j]||sqrt<prime[j]) break;
			if(!(a[i]%prime[j])){
				num=0;
				while(!(a[i]%prime[j])) a[i]/=prime[j],num++;
				if(num<min[prime[j]]) min2[prime[j]]=min[prime[j]],min[prime[j]]=num;
				else if(num<min2[prime[j]]) min2[prime[j]]=num;
				vis[prime[j]]++;
			}
		}
		if(a[i]>1){
			if(1<min[a[i]]) min2[a[i]]=min[a[i]],min[a[i]]=1;
			else if(1<min2[a[i]]) min2[a[i]]=1;
			vis[a[i]]++;
		} 
	}
	for(reg int i=2;i<=200000;i++)
		if(vis[i]==n) for(reg int j=1;j<=min2[i];j++) ans*=i;
		//每个数都包含这个质数,所以用 min2 
		else if(vis[i]==n-1) for(reg int j=1;j<=min[i];j++) ans*=i;
		//有一个数不包含它,不包含它的那个数是 0,所以 min 其实才是第二小的
	std::printf("%lld",ans);
	return 0;
}

CF1349 B. Orac and Medians

https://codeforces.com/problemset/problem/1349/B
给出一列数 (a),每次可以将 (a) 的一个区间改成这个区间数的中位数,问能不能将数列全都变成 (k)(给定的)
此处,偶数长度序列的中位数,是中间靠左的那个数

如果没有 (k),肯定不行
(k),且 (k) 旁边有一个大于等于 (k) 的数,那么可以选这两个数的区间,让它变成两个连续的 (k),然后一个一个扩大区间,最终使得整个序列都变成 (k)

那么考虑怎么让 (k) 旁边出现大于等于 (k) 的数
如果有两个大于等于 (k) 的数相距不超过 (2),也就是中间最多夹一个其他数,那么一定可以
因为如果是这种情况,则一定可以向上面描述的那样,一个一个的扩大区间,使得任意包含这两个数的区间都变成这两个数,那么就可以让 (k) 旁边出现大于等于 (k) 的数
否则一定不行,因为任意一个长度为 (m) 的区间,如果包含 (k),并且想让 (k) 作为它的中位数,则大于等于 (k) 的数至少有 (ceilfrac{m}{2} ceil)
所以如果任意两个大于等于 (k) 的数相距都大于 (2),则在任意长度为 (m) 的区间内,不可能出现 (ceilfrac{m}{2} ceil) 个大于等于 (k) 的数

所有是个结论题

int a[100005];
int main(){int T=read();while(T--){
	int n=read(),k=read(),flag=0;
	for(reg int i=1;i<=n;i++){
		a[i]=read();
		if(a[i]==k) flag=1;
	}
	a[n+1]=a[n+2]=a[n+3]=0;
	if(!flag){
		std::puts("no");continue;
	}
	if(n==1&&a[1]==k) goto YES;
	for(reg int i=1;i<=n;i++)if(a[i]>=k){
		if(a[i+1]>=k||a[i+2]>=k) goto YES;
	}
	std::puts("no");continue;
	YES:;
	std::puts("yes");
}
	return 0;
}

CF1349 C. Orac and Game of Life

https://codeforces.com/problemset/problem/1349/C
给出一个 (n imes m) 的格子,每个格子是 (1)(0)
每次操作,对于每个格子,如果周围有和他一样数字的格子,就改变数字,如果没有,不变

把格子分成两类,第一类是周围有和他一样数字的格子,第二类是没有

  • 第一类格子永远是第一类,因为它旁边相同颜色的格子也是第一类,会跟他一起变动,所以它旁边一直有和他数字相同的格子
  • 如果第二类旁边有一个第一类,那么第一类会在一次变动后改变颜色,此时,它应该和第二类的这个格子数字相同(因为一开始是不同的),那么,这个第二类就变成了第一类
  • 那么第一类的格子每次会将它旁边的格子变成第一类,所以对于一个第二类的格子,它变成第一类所需要的步数,就是和它曼哈顿距离最小的一个第一类格子的曼哈顿距离

所以,我们只要 bfs 出这个所需的步数,然后就是每操作一次,就改变一次,可以用异或得出

int a[1006][1006],type[1006][1006];
int num[1006][1006];
int q[3000006],head,tail,vis[1006][1006];
int n,m;
char s[1006];
const int dx[4]={1,-1,0,0};
const int dy[4]={0,0,1,-1};
inline int no(int x,int y){
	if(x<1||y<1||x>n||y>m) return 1;
	if(vis[x][y]) return 1;
	return 0;
}
inline int check(int i,int j){
	if(i-1>0&&a[i][j]==a[i-1][j]) return 1;
	if(i+1<=n&&a[i+1][j]==a[i][j]) return 1;
	if(j-1>0&&a[i][j-1]==a[i][j]) return 1;
	if(j+1<=m&&a[i][j]==a[i][j+1]) return 1;
	return 0;
}
inline void bfs(){
	reg int x,y,x_,y_,step;
	while(tail<=head){
		x=q[tail++];y=q[tail++];step=q[tail++]+1;
		for(reg int k=0;k<4;k++){
			x_=x+dx[k];y_=y+dy[k];
			if(no(x_,y_)) continue;
			vis[x_][y_]=1;
			if(type[x_][y_]==2) num[x_][y_]=step;
			q[++head]=x_;q[++head]=y_;q[++head]=step;
		}
	}
}
int main(){
	n=read();m=read();int Q=read();
	for(reg int i=1;i<=n;i++){
		std::scanf("%s",s+1);
		for(reg int j=1;j<=m;j++) a[i][j]=s[j]-48;
	}
	head=0;tail=1;
	for(reg int i=1;i<=n;i++)for(reg int j=1;j<=m;j++){
		if(check(i,j)) type[i][j]=1,q[++head]=i,q[++head]=j,q[++head]=0,vis[i][j]=1;
		else type[i][j]=2;
	}
	bfs();
	reg int x,y;LL p;
	while(Q--){
		x=read();y=read();p=read();
		if(type[x][y]==1) std::printf("%d
",a[x][y]^(p&1));
		else{
			if(!num[x][y]) std::printf("%d
",a[x][y]);//永远不会变
			else if(p<=num[x][y]) std::printf("%d
",a[x][y]);
			else std::printf("%d
",a[x][y]^((p-num[x][y])&1));
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/12880636.html