Codeforces 近期题目

CodeForces 近期 Div.2 题目

CF1776-D. Lucky Chains

简要题意:

\(n\) 组数据 (\(1 \le n \le 10^6\)),每组输入两个数 \(x,y,(1\le x,y \le 10^7)\)

找一个最小的自然数 \(k\) ,使得 \(\gcd(x+k,y+k)\neq1\) ,如果找不到,输出 \(-1\)

Idea:

考虑 $x $ 和 \(y\) 的差 \(d\)

由题意,不妨设 \(m |(x+k)\)\(m|(y+k)\)

有整除的可见性,得到 \(m|(x+k-y-k)\)\(m|d\)

相当于在 \(d\) 的因子中找一个 \(m\) ,使得 \(k\) 最小。

形式化地,有

\[opt(x,y)=\min_{m|d}(x+m-1)/m*m-x \]

枚举 \(d\) 的因子 \(m\) ,我们得到了一种 \(O(n\sqrt{d})\) 的做法。

Solution:

上面的做法不能通过本题,考虑优化。

考虑 \(d\) 的两个质因子 \(p,t\) (假设有),发现 \(m\)\(p\)\(t\) 时的解肯定不劣于 \(m\)\(pt\) 时的,因为 \(pt|x+k\) 时同时满足 \(p|x+k\)\(t|x+k\) ,反之则不一定。所以只枚举 \(d\) 的质因数即可。

如果我们知道值域内每个数的一个质因数,则枚举 \(d\) 的质因子这一问题可以通过递归解决,时间复杂度 \(O(\log w)\) ( \(w\) 为值域,\(1 \le w \le 1e7\) ) 。

预处理值域内每个数的一个质因数可以通过埃氏筛解决,时间复杂度 \(O(w \log w)\)

至此,本问题在 \(O(w \log w + n \log w)\) 时间内解决。

Code:

代码很短。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+100;
int a[maxn];
void init(){
	for(int i=1;i<maxn;i++) a[i]=i;
	for(int i=2;i<maxn;i+=2){
		a[i]=2;
	}
	for(int i=3;i<maxn;i+=2){
		if(a[i]!=i){
			continue;
		}
		for(int j=3;i*j<maxn;j+=2){
			a[i*j]=i;
		}
	} 
} 
int opt(int x,int y){
	int d=y-x;
	int ans=1e9+100;
	if(d==1){
		return -1;
	}
	while(d>1){
		ans=min(ans,a[d]-x%a[d]);
		if(x%a[d]==0){
			ans=0;
			break;
		}
		d/=a[d];
	}
	return ans;
}
int main(){
	init();
	int t;
	scanf("%d",&t);
	int x,y;
	while(t--){
		scanf("%d%d",&x,&y);
		printf("%d\n",opt(x,y));
	}
	return 0;
}

[CF1762-D. GCD Queries](Problem - D - Codeforces)

简要题意:

有一个 \([0,1,\dots ,n-1]\) 的排列 \(P\) ,和 \(2n\) 次询问,每次你可以询问两个数,交互库给出这两个数的最大公因数,询问结束后,输出两个数 \(x,y\) ,满足 \(P_x=0\)\(P_y=0\)

Solution:

考虑简单情况:假设只有 \(3\) 个数:\(P_i,P_j,P_k\) ,其中包含 \(0\) ,询问几次才可以找出答案?

\(2\) 次。

第一次:\((P_i,P_j)\) ,记答案为 \(x\)

第二次:\((P_i,P_k)\) ,记答案为 \(y\)

如果 \(x=y\) ,那么 \(P_i\neq 0\) ,因为 \(\gcd(0,t)=t\) ,序列中数各不相同。

如果 \(x <y\) ,那么 \(P_j\neq0\) ,因为如果 \(P_j=0\)\(gcd(P_k,P_i)\le P_i=gcd(P_i,P_j)\)

如果 \(x>y\) ,那么 \(P_k \neq0\)

我们发现,对于三个数,通过两次操作总能排除一个数。

我们最后需要排除 \((n-2)\) 个不是 \(0\) 的数,所以只需要 \((2n-4)\) 次就可以解决原问题。

Code:

#include<bits/stdc++.h>
using namespace std;
vector<int> al;
int main(){
	int t;
	cin>>t;
	while(t--){
		al.clear();
		int n;
		cin>>n;
		for(int i=1;i<=n;i++) al.push_back(i);
		while(al.size()>2){
			cout<<"? "<<al[0]<<" "<<al[1]<<endl;
			cout.flush();
			int x,y;
			cin>>x;
			cout<<"? "<<al[0]<<" "<<al[2]<<endl;
			cout.flush();
			cin>>y;
			if(x==y) al.erase(al.begin());
			if(x>y) al.erase(al.begin()+2);
			if(x<y)	al.erase(al.begin()+1);
		}
		if(al.size()==1) cout<<"! "<<al[0]<<" "<<al[0]<<endl;
		else cout<<"! "<<al[0]<<" "<<al[1]<<endl;
		cout.flush();
		int x;
		cin>>x;
	}
} 

CF1758-D.Range = √Sum

简要题意:

给你一个整数 \(n\) ,找出一个序列 \(a\) 使得

\[\max(a_1, a_2, \dots, a_n) - \min(a_1, a_2, \dots, a_n)= \sqrt{a_1 + a_2 + \dots + a_n} \]

其中 \(n \le 3⋅10^5\)

Solution:

构造题。

如果 \(n\) 为偶数,可以构造为 \((\frac{1}{2}n,\frac{1}{2}n+1,\dots,n-1,n+1,\dots,\frac{3}{2}n)\)

如果 \(n\) 为奇数,只需要在上面的形式下微调,结合 \((n+1)^2=n^2+2n+1\) ,令最大值与最小值的差值为 \(n+1\) ,在左边每个数补上 \(2\) ,再微调即可。

例如 \(n=5\) 时,序列的构造方法:

\[\begin{aligned} &[3,4,5,6,7](平均数为n) \\ &[2,4,5,6,8](令最大值与最小值的差为 (n+1)) \\ &[4,6,7,8,10](数列和加上 2n) \\ &[4,6,7,9,10](补上1) \end{aligned} \]

Code:

#include<bits/stdc++.h>
using namespace std;
void doe(int n){
	if(n%2==1){
		for(int i=n-n/2+2;i<=n+n/2+2;i++){
			if(i==n-n/2+2) cout<<i-1<<" ";
			if(i==n+n/2+1) cout<<i+1<<" ";
			if(i==n+n/2+2) cout<<i+1<<" ";
			if(i>n-n/2+2&&i<n+n/2+1) cout<<i<<" ";
		}
		cout<<endl;
		return;
	}else{
		for(int i=n/2;i<=n+n/2;i++) if(i!=n) cout<<i<<" ";
		cout<<endl;
		return ;
	} 
}
int main(){
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		doe(n);
	}
} 

CF1762-E.Tree Sum

简要题意:

定义一棵生成树 \(T\) 是好的,当且仅当每条边的边权为 \(1\)\(-1\) 且每个点的出边边权乘积为 \(-1\)

给定一个整数 \(n\) ,求 \(n\) 的所有本质不同的好的生成树中点 \(1\) 到点 \(n\) 的路径长的和,答案对 \(998,244,353\) 取模。

Idea:

观察可知,当 \(n\) 为奇数时,这样的树是不存在的。

Proof:

设点 \(i\) 出边的乘积为 \(f(i)\) ,则对于树上每个点,都有 \(f(i)=-1\) ,才能成立。

\(\therefore \Pi_{i=1}^{n}f(i)=-1\)

然而使用边的贡献计算,由于总共有偶数条边 \(\therefore \Pi_{i=1}^{n}f(i)=1\) ,矛盾。

\(n\) 为奇数时答案为 \(0\) ,下面考虑 \(n\) 为偶数。

Hint:

手玩几棵树之后,可以发现对于 \(n\) 的一个确定的无边权生成树,边权的填法是唯一的。(这里我不太会严谨证,但是应该比较显然QwQ)

Solution:

\(Hint\) 类似的结论,对于树上的一条边,假设它左边有 \(l\) 个点,右边有 \((n-l)\) 个点,那么这条边的权值是 \((-1)^l\)

由上面这些结论,计算答案可以考虑使用计算贡献的方法。

\[\sum d(1,n)=(-1)^l·p \]

其中 \(p\) 表示在路径上的边的条数,下面我们计算 \(p\)

设这一条我们考虑的路径上的边为 \(e\) , \(p\) 即表示 \(e\) 的种类数。

e 将这 \(n\) 个点分成两部分,不妨设一部分有 \(l\) 个点,另一部分有 \(r\) 个点 \((l+r=n)\)

由于 \(e\) 在路径上,所以点 \(1\) 和点 \(n\) 分别处于 \(e\) 的两部分,不妨设点 \(1\) 这一侧有 \(l\) 个点。

选出 \(n\) 个点中剩下的在点 \(1\) 这一侧的点的情况数,有 \(C_{n-2}^{l-1}\) 种。

断开 \(e\) 后,这两侧分别变成一棵分别有 \(l,r\) 个点的生成树,根据 \(Caley\) 公式,分别有 \(l^{l-2},r^{r-2}\) 种方案。

\(e\) 连接这两个树上任意一点,有 \(r·l\) 中选法。

所以对于确定的 \(l\) ,方案数为 \(C_{n-2}^{l-1}·l^{l-2}·r^{r-2}·r·l\)

\[\therefore \sum{d(1,n)}=\sum_{l=1}^{n}C_{n-2}^{l-1}·l^{l-2}·r^{r-2}·r·l \]

预处理组合数(要用到乘法逆元、快速幂)后计算即可。

时间复杂度 \(O(n\log n)\)

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int ksm(int a,int b)
{
	if(b==-1) return 1;
	int ans=1;
  	a=(a%mod+mod)%mod;
  	for(;b;b>>=1){
    	if(b&1) ans=(a*ans)%mod;
    	a=(a*a)%mod;
  	}
	return ans;
}
int inverse(int a){
	return ksm(a,mod-2)%mod;
}
int combine[500010];
void init(int n){
	combine[0]=1;
	combine[1]=n;
	for(int i=2;i<=n;i++){
		combine[i]=(combine[i-1]*(n-i+1))%mod*inverse(i)%mod;
	}
}
signed main(){
	int n;
	cin>>n;
    init(n-2);
	if(n%2==1){
		cout<<"0";
		return 0;
	}
	int ans=0;
	for(int l=1;l<n;l++){
		int r=n-l;
		ans=(ans+(-1*2*(l%2)+1)*combine[l-1]*l%mod*r%mod*ksm(l,l-2)%mod*ksm(r,r-2)%mod)%mod;
		ans=(ans+mod)%mod;
	}
	cout<<ans<<endl;
	return 0;
} 

posted @ 2022-12-17 18:01  ArizonaYYDS  阅读(74)  评论(0)    收藏  举报