Codeforces Round #641 (div.2) 题解

Codeforces Round #641 (div.2)。由于是 Chinese Round,慕名来补了一下。

哦对了这个 F 中国场特色啊,不会。以后学会了再补。

A. Orac and Factors

题目大意

  • 多测,\(T\) 组数据。
  • 定义 \(f(x)\)\(x\) 的最小非平凡正因子,也就是比 \(1\) 大的最小因子。
  • 每次询问会给定 \(n\)\(k\) 两个值。每次操作表示对 \(n\) 加上 \(f(n)\)注意到每次操作后 \(n\) 的值会变化。
  • 请你求出对 \(n\) 进行 \(k\) 次操作后 \(n\) 的值。
  • 数据范围 \(1\leq T\leq 100\)\(1\leq n\leq 10^6\)\(1\leq \sum n\leq 10^6\)\(k\leq 10^9\)

分析

注意到 \(k\leq 10^9\),显然不能模拟每一次操作。

考虑一次操作会对 \(n\) 产生什么影响。

  • 如果 \(2|n\),那么很好办,假设后面还剩下 \(k_0\) 次操作要做,那么最终结果就是 \(n+2k_0\)
  • 如果 \(2\not | n\),那么 \(2\not | f(n)\) 一定成立,那么 \(2|n+f(n)\)。这样又与第一种情况相同。

所以在经过一次操作后,无论最初取的 \(n\) 为何值,总会变为偶数。我们得到:

\[\operatorname{ans}=\begin{cases}n+2k & 2|n \\ n+f(n)+2(k-1) & 2\not|n \end{cases} \]

按照该式模拟即可。复杂度 \(\Theta(\sum n)\)

代码

#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#define int long long
HohleFeuerwerke;
inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar('0'+x%10);
}
inline int frac(int x){
	for(int i=3;i*i<=x;i++){
		if(x%i==0) return i;
	}
	return x;
}
signed main()
{
	int T=read();
	while(T--){
		int n=read(),k=read();
		if(n%2==0) write(n+2*k),puts("");
		else write(n+frac(n)+2*(k-1)),puts("");
	}
}

B. Orac and Models

题目大意

  • 多测,\(T\) 组数据。
  • 有一个长度为 \(n\) 的序列 \(a\)
  • 定义一个“美丽的”子序列 \(s\) 为,单调上升,且对于任意 \(1\leq j\leq i\leq n\) 满足 \(i=j+1\) 在原序列中的数单调上升,即:

\[a_i>a_j \]

且在原序列中的位置 \(pos_i\)\(pos_j\) 呈倍数关系,即:

\[pos_j|pos_i \]

  • 求有最长“美丽”的子序列的长度。
  • 举例:\(\{5,3,4,6\}\) 有两个长度为 \(2\) 的“美丽”的子序列,分别是 \(\{5,6\}\)\(\{3,6\}\)
  • 数据范围:\(T\leq 100\)\(1\leq n\leq 10^5\)\(1\leq a_i\leq 10^9\)

分析

显然是 \(\operatorname{dp}\)。状态也很好定义:令 \(f_i\) 表示以 \(i\) 结尾的最长“美丽”序列的长度。

考察一个位置的 \(f_i\) 若已经计算好了,将能对多少地方的计算做出贡献:显然只能对 \(i\) 的倍数的 \(f_ik\) 的计算做出贡献。

方程很好写:

\[f_i=\max\{f_j,j|i\}+1 \]

如果每次计算枚举 \(i\) 的因数,那么复杂度是 \(\Theta(n\sqrt n)\);如果每次计算枚举 \(i\) 的倍数进行贡献,那么复杂度是 \(\Theta(n\log n)\)

具体一点,我们已经求出了 \(f_i\),那么我们只需要计算一个 \(f_j\)\(j\) 的初值设为 \(2\times i\),上限也是 \(n\),每次进行 \(f_j\) 的 update,即让 \(f_j\) 取已经存下的最大值和新来的最大值中较大的一个,然后就让 j+=i 即可。

代码

#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
//#define int long long
HohleFeuerwerke;
inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e6+5; int n; int a[MAXN]; int dp[MAXN];
signed main()
{
	int T=read();
	while(T--){
		n=read(); int ans=0;
		for(int i=1;i<=n;i++) a[i]=read(),dp[i]=1;
		for(int i=1;i<=n;i++)
			for(int j=2*i;j<=n;j+=i){
				if(a[i]<a[j]) dp[j]=max(dp[i]+1,dp[j]);
			}
		for(int i=1;i<=n;i++) ans=max(ans,dp[i]);
		printf("%d",ans);puts("");
	}
}

C. Orac and LCM

题目大意

  • 有一个长度为 \(n\) 的序列 \(a\)
  • 给定可重集 \(S=\{\operatorname{lcm}(a_i,a_j) \ |\ 1\leq i<j\leq n \}\)
  • 定义一个集合的 \(\gcd\) 为最大的 \(d\) 满足对于任意的集合元素 \(x\) 都有 \(d\ |\ x\)
  • 请你求出 \(\gcd(S)\)
  • 数据范围:\(1\leq n\leq 10^5\)\(1\leq a_i\leq 5\times 10^5\)

分析

考虑一个 \(\operatorname{lcm}(a,b)=\frac{a\times b}{\gcd(a,b)}\),可以将原式转化为可以预处理的后缀 \(\gcd\) 和总 \(\gcd\) 的形式。

\[\operatorname{Ans}\ =\ \frac{\gcd\{a_i\times a_j\ |\ i<j \}}{\gcd(S)} \]

而上面的分子可以用后缀 \(\gcd\) 预处理,所以总复杂度是 \(\Theta(n\log \max\{a_i\})\)

代码

#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#define int long long
HohleFeuerwerke;
inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar('0'+x%10);
}
inline int gcd(int x,int y){
	if(!y) return x;
	return gcd(y,x%y);
}
const int MAXN=1e5+5; int n; int a[MAXN]; int gcds[MAXN];
int ans=0;
signed main()
{
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=n;i>=1;i--) gcds[i]=gcd(gcds[i+1],a[i]);
	for(int i=1;i<=n;i++) ans=gcd(ans,a[i]*gcds[i+1]);
	ans/=gcds[1]; write(ans); puts("");
}

D. Orac and Median

题目大意

  • 给定序列 \(a\),问能否通过若干次操作使得所有的数都变为给定的 \(k\)
    • 一次操作是指:对给定区间全部赋值为其中位数。特别地,如果区间中有偶数个数,则取中位的较小值。
  • 数据范围:\(1\leq n\leq 10^5\)\(1\leq k\leq 10^9\)\(1\leq a_i\leq 10^9\)

分析

我们可以分以下几种情况考虑:

  • 考虑一个没有 \(k\) 的序列,显然答案为 No
  • 考虑一个长度为 \(1\) 的序列,显然答案就是 \(a_1=k\)
  • 考虑一个有 \(k\) 的序列,如果存在一个 \(2\sim n-1\) 的下标,满足他的左右都比 \(k\) 大,那么答案显然为 Yes。因为此时可以通过赋值这三个数得到一段 \(\geq k\) 的区间,然后一点一点往左挪就行了。
  • 考虑一个有 \(k\) 的序列,如果存在一个 \(2\sim n\) 的下标,满足他本身和左边比 \(k\) 大,那么答案也显然为 Yes。因为此时已经存在了一个 \(\geq k\) 的区间。

代码

#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#define int long long
HohleFeuerwerke;
inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar('0'+x%10);
}
int T;
int n,k; const int MAXN=1e5+5; int a[MAXN];
signed main()
{
	T=read();
	while(T--){
		n=read(); k=read(); bool flag=false; bool answered=false;
		for(int i=1;i<=n;i++) a[i]=read();
		for(int i=1;i<=n;i++) if(a[i]==k) flag=true;
		if(!flag) puts("no"),answered=true;
		if(n==1&&a[1]==k) puts("yes"),answered=true;
		for(int i=2;i<=n-1;i++){
			if((a[i-1]>=k)&&(a[i+1]>=k)&&(!answered)) puts("yes"),answered=true;
		}
		for(int i=2;i<=n;i++){
			if((a[i-1]>=k)&&(a[i]>=k)&&(!answered)) puts("yes"),answered=true;
		}
		if(!answered) puts("no");
	}
}

E. Orac and Game of Life

题目大意

  • 有一个 \(n\times m\)\(0 \& 1\) 矩阵。初始时间为 \(0\),给定初始时间的这个矩阵 \(A\)
  • 每过一个时刻,矩阵都会发生如下变化:
    • 考虑一个格子,如果它的上下左右存在和它颜色相同的格子,则它会在下一个时刻取反,\(0\to 1\)\(1\to 0\)
  • \(T\) 次询问,每次询问给定三个数 \(p,x,y\),表示询问第 \(p\) 个时刻坐标为 \((x,y)\) 的格子的颜色是啥。

分析

我们只需要知道每一个格子在哪一个时刻并入了哪一个颜色的连通块,就可以知道它在 \(p\) 时刻的状态了。并且在它并入连通块之前他是不会改变颜色的。

我们只需要找到原来就存在的连通块,然后一层一层往外扩展就可以了。这可以用 \(\operatorname{bfs}\) 来实现。

代码

#include<bits/stdc++.h>
#define HohleFeuerwerke using namespace std
#pragma GCC optimize(3,"Ofast","-funroll-loops","-fdelete-null-pointer-checks")
#pragma GCC target("ssse3","sse3","sse2","sse","avx2","avx")
#define int long long
HohleFeuerwerke;
inline int read(){
	int s=0,f=1;char c=getchar();
	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	for(;isdigit(c);c=getchar()) s=s*10+c-'0';
	return s*f;
}
inline char getc(){
	char c=getchar();
	while(c!='0'&&c!='1') c=getchar();
	return c;
}
inline void write(int x){
	if(x<0) putchar('-'),x=-x;
	if(x>=10) write(x/10);
	putchar('0'+x%10);
}
const int MAXN=1e3+5,MAXM=1e3+5;
int n,m,T;
char a[MAXN][MAXM],clr[MAXN][MAXN];
int t[MAXN][MAXM];
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
struct node{
	int x,y; int time;
};
inline void bfs(){
	queue<node>q;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++){
			bool flag=false;
			for(int k=0;k<4;k++) if(a[i+dx[k]][j+dy[k]]==a[i][j]) flag=true;
			if(flag) t[i][j]=0,clr[i][j]=a[i][j],q.push({i,j,0});
		}
	while(!q.empty()){
		node x=q.front();q.pop();
		int curx=x.x,cury=x.y,curtime=x.time;
		for(int i=0;i<4;i++){
			int tx=dx[i]+curx,ty=dy[i]+cury;
			if(1<=tx&&tx<=n&&1<=ty&&ty<=m&&t[tx][ty]==-999){
				q.push({tx,ty,curtime+1}); t[tx][ty]=curtime+1;
				if(a[curx][cury]=='0') clr[tx][ty]='1';
				if(a[curx][cury]=='1') clr[tx][ty]='0';
			}
		}
	}
}
inline int answer(int tp,int x,int y){
	if(tp<t[x][y]||t[x][y]==-999) return a[x][y]-'0';
	else{
		tp-=t[x][y];
		if(tp%2==0) if(clr[x][y]=='1') return 1; else return 0;
		if(tp%2==1) if(clr[x][y]=='1') return 0; else return 1;
	}
}
signed main()
{
	n=read(),m=read(),T=read();
	for(int i=1;i<=n;i++) 
		for(int j=1;j<=m;j++) 
			a[i][j]=getc(),clr[i][j]=a[i][j],t[i][j]=-999;
	bfs();
	while(T--){
		int x=read(),y=read(),p=read();
		write(answer(p,x,y));puts("");
	}
}
posted @ 2020-12-26 22:49  _HofFen  阅读(172)  评论(1编辑  收藏  举报