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\) 最小。
形式化地,有
枚举 \(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\) 使得
其中 \(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\) 时,序列的构造方法:
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\) 。
由上面这些结论,计算答案可以考虑使用计算贡献的方法。
其中 \(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\) 。
预处理组合数(要用到乘法逆元、快速幂)后计算即可。
时间复杂度 \(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;
}

浙公网安备 33010602011771号