Codeforces Round #641 (Div. 2) Solution (F学了生成函数再回来淦)

前言

第一次和师兄一起在机房打比赛,听着他们噼里啪啦的键盘声,自己不觉也打得更快了。

rating:1739->1880

A A A

f ( n ) 为 n 的 最 小 质 因 数 f(n)为n的最小质因数 f(n)n,重复t次如下操作:n+=f(n); 输出最终答案

显然 f ( n ) + n f(n)+n f(n)+n一定为偶数,所以后面都是+2.

int n,T,prime[N],tot,f[N];
void get() {
	for(int i=2;i<N;i++) {
		if(!f[i]) f[i]=i,prime[++tot]=i;
		for(int j=1,k;(k=i*prime[j])<N;j++) 
			if(i%prime[j]) f[k]=prime[j];
			else {f[k]=f[i];break;}
	}
}

int main() { get();qr(T); while(T--) {
		qr(n); ll k; qr(k);
		pr2(n+f[n]+(k-1)*2);
	}
	return 0;
}

貌似埃筛即可,打啥线性筛啊.

B B B

给定一个长度为n的数组a,求最长的序列 b 1 , b 2 . . . b k b_1,b_2...b_k b1,b2...bk满足 ∀ i ∈ [ 1 , k ) ∩ N , a [ b i ] < a [ b i 1 ] , a [ b i ] ∣ a [ b i + 1 ] \forall i\in [1,k)\cap \N,a[b_i]i[1,k)N,a[bi]<a[bi1],a[bi]a[bi+1].

定义 f [ i ] 表 示 以 i 结 尾 的 最 长 序 列 , 每 次 直 接 枚 举 约 数 即 可 f[i]表示以i结尾的最长序列,每次直接枚举约数即可 f[i]i,

int T,n,ans,a[N],f[N];


int main() {
	qr(T); while(T--) {
		qr(n); ans=0;
		for(int i=1;i<=n;i++) {
			qr(a[i]); f[i]=1; 
			for(int j=1;j*j<=i;j++) 
				if(i%j==0) {
					if(a[i]>a[j]) f[i]=max(f[i],f[j]+1);
					if(a[i]>a[i/j]) f[i]=max(f[i],f[i/j]+1);
				}
			ans=max(ans,f[i]);
		}
		pr2(ans);
	}
	return 0;
}

C C C

gcd ⁡ { l c m ( a i , a j ) } ( j < i ) \gcd \{lcm(a_i,a_j)\}(jgcd{lcm(ai,aj)}(j<i).

方法1

我比赛的时候想到的做法.

l c m ( a i , a j ) = a i a j d = gcd ⁡ ( a i , a j ) lcm(a_i,a_j)=\dfrac{a_i a_j}{d=\gcd(a_i,a_j)} lcm(ai,aj)=d=gcd(ai,aj)aiaj

对于确定的d,我们再对 满 足 d ∣ a j 的 a j 求 一 遍 gcd ⁡ 即 可 满足d|a_j的a_j求一遍\gcd即可 dajajgcd

也就是当我们扫到 j j j的时候,就先钦定它和后面数的gcd(枚举约数).

当扫到 i i i的时候,只需枚举约数 d d d,求一下 a i gcd ⁡ ( a j ) / d , ( d ∣ a j ) a_i \gcd(a_j)/d,(d|a_j) aigcd(aj)/d,(daj).

复杂度: O ( n m x a [ i ] O(n\sqrt mxa[i] O(nm xa[i]

ll n,a[N],v[N],ans;

int main() {
	qr(n);
	for(int i=1;i<=n;i++) {
		qr(a[i]);
		for(int j=1;j*j<=a[i];j++)
			if(a[i]%j==0) {
				ans=gcd(ans,a[i]*v[j]);
				ans=gcd(ans,a[i]*v[a[i]/j]);
				v[j]=gcd(v[j],a[i]/j);
				v[a[i]/j]=gcd(v[a[i]/j],j);
			}
	}
	pr2(ans);
	return 0;
}

方法2

gcd ⁡ ( l c m ( a 1 , a i ) , l c m ( a 2 , a i ) . . . l c m ( a i − 1 , a i ) ) = l c m ( a i , gcd ⁡ ( a 1 , . . a i − 1 ) ) \gcd(lcm(a_1,a_i),lcm(a_2,a_i)...lcm(a_{i-1},a_i))=lcm(a_i,\gcd(a_1,..a_{i-1})) gcd(lcm(a1,ai),lcm(a2,ai)...lcm(ai1,ai))=lcm(ai,gcd(a1,..ai1))

证明:因为lcm,gcd分别是对质因子的指数取max和min.所以我们只用考虑每个质因子的指数即可.

设对于一个质数 p p p, a [ i ] 的 对 应 的 指 数 为 c i a[i]的对应的指数为c_i a[i]ci.

则我们需要证明: min ⁡ { max ⁡ ( c i , c 1 ) , max ⁡ ( c i , c 2 ) . . . } = max ⁡ { c i , min ⁡ ( c 1 . . c i − 1 ) } \min\{\max(c_i,c_1),\max(c_i,c_2)...\}=\max\{c_i,\min(c_1..c_{i-1})\} min{max(ci,c1),max(ci,c2)...}=max{ci,min(c1..ci1)}

这个显然是对的,你只需要讨论一下 min ⁡ ( c 1 . . . c i − 1 ) 与 c i 的 大 小 即 可 \min(c_1...c_{i-1})与c_i的大小即可 min(c1...ci1)ci

int n;
ll ans,g,x;

int main() {
	qr(n);
	for(int i=1;i<=n;i++) {
		qr(x); 
		if(i>1) ans=gcd(ans,lcm(g,x));
		g=gcd(g,x);
	}
	pr2(ans);
	return 0;
}

方法3

对每个质数p考虑贡献,如果我们得到了最小指数和次小指数 t t t,则 a n s = ∏ p t ans=\prod p^t ans=pt.

线性筛求A中的f即可.

#include 
#include 
#include 
using namespace std;
#define maxn 1000010
#define int long long

int n,ans=1;
int prime[maxn],t=0;
bool v[maxn];
void work()
{
	for(int i=2;i<=maxn-10;i++)
	{
		if(!v[i])prime[++t]=i;
		for(int j=1;j<=t;j++)
		{
			if(i*prime[j]>maxn-10)break;
			v[i*prime[j]]=true;
			if(i%prime[j]==0)break;
		}
	}
}
int S[maxn],m1[maxn],m2[maxn];
void update(int x,int y)
{
	if(y<m1[x])m2[x]=m1[x],m1[x]=y;
	else if(y<m2[x])m2[x]=y;
}
void go(int x)
{
	for(int i=1;v[x];i++)
	if(x%prime[i]==0)
	{
		int tot=0;S[prime[i]]++;
		while(x%prime[i]==0)x/=prime[i],tot++;
		update(prime[i],tot);
	}
	if(x!=1)update(x,1),S[x]++;
}
int ksm(int x,int y)
{
	int re=1;
	while(y)
	{
		if(y&1)re*=x;
		x*=x;y>>=1;
	}
	return re;
}

signed main()
{
	scanf("%lld",&n);work();
	memset(m1,63,sizeof(m1));memset(m2,63,sizeof(m2));
	for(int i=1,x;i<=n;i++)scanf("%lld",&x),go(x);
	for(int i=1;i<=t;i++)if(S[prime[i]]==n)ans*=ksm(prime[i],m2[prime[i]]);
	else if(S[prime[i]]==n-1)ans*=ksm(prime[i],m1[prime[i]]);
	printf("%lld",ans);
}

D D D

神奇讨论题.

先转换一下

b [ i ] = { 0    a [ i ] < k 1    a [ i ] = k 2    a [ i ] > k b[i]=\begin{cases}0~~ a[i]k\end{cases} b[i]=0  a[i]<k1  a[i]=k2  a[i]>k

然后我们的目标是把全变成1.

首先无1一定不行.

然后,如果存在{1,1},{1,2},{2,2}都一定能成功.

{1,1}可以无限拓展.

{1,2}可以一步变成{1,1}

{2,2}可以无限拓展,一遇到1就可以生成{1,1}

所以代码特短.


int T,n,k,a[N];

bool pd() {
	bool flag=0;
	for(int i=1;i<=n;i++) flag|=a[i]==1;
	if(!flag) return 0;
	if(n==1) return 1;
	for(int i=1;i<=n;i++)
		if(a[i]&&(a[i+1]||a[i+2])) return 1;
	return 0;
}

int main() {
	qr(T); while(T--) {
		qr(n); qr(k);
		for(int i=1;i<=n;i++) {
			qr(a[i]);
			if(a[i]<k) a[i]=0;
			else if(a[i]==k) a[i]=1;
			else a[i]=2;
		}
		a[n+1]=a[n+2]=0;
		puts(pd()?"yes":"no");
	}
	return 0;
}

E E E

可以发现联通块可能会越来越少,

除了类似

0101

1010

这种情况.

所以我们 b f s bfs bfs一遍求出每个格子最早被弄如联通块(大小>1)的时间,然后要么一直变要么一直不变.


int n,m,t,d[N][N];
char a[N][N];
const int dx[]={1,-1,0,0},dy[]={0,0,1,-1};
pair<int,int> q[N*N];int l,r;bool vis[N][N];

int main() {
	qr(n); qr(m); qr(t);
	for(int i=1;i<=n;i++) {
		scanf("%s",a[i]+1);
	}
	l=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++) {
			bool f=0;
			for(int v=0;v<4;v++) {
				int tx=i+dx[v],ty=j+dy[v];
				if(0<tx&&tx<=n&&0<ty&&ty<=m&&a[i][j]==a[tx][ty]) f=1;
			}
			if(f) q[++r]=mk(i,j),vis[i][j]=1;
		}
	while(l<=r) {
		int x=q[l].fi,y=q[l].se; l++;
		for(int v=0;v<4;v++) {
			int tx=x+dx[v],ty=y+dy[v];
			if(0<tx&&tx<=n&&0<ty&&ty<=m&&!vis[tx][ty]) {
				vis[tx][ty]=1;
				q[++r]=mk(tx,ty);
				d[tx][ty]=d[x][y]+1;
			}
		}
	}
	while(t--) {
		ll x,y,z; qr(x); qr(y); qr(z);
		if(d[x][y]>=z) putchar(a[x][y]);
		else putchar(a[x][y]^((r>0)*(z-d[x][y])&1));
		puts("");
	}
	return 0;
}


F F F

不会,占坑.

你可能感兴趣的:(比赛)