Codeforces Round #781 (Div. 2)(ABCD)
A. GCD vs LCM
题意:
给定一个n,将n分成4部分,满足
a
+
b
+
c
+
d
=
n
&
&
g
c
d
(
a
,
b
)
=
=
l
c
m
(
c
,
d
)
a+b+c+d=n\&\&gcd(a,b)==lcm(c,d)
a+b+c+d=n&&gcd(a,b)==lcm(c,d)
思路:
先判断n是否为4的倍数,是的话分成4等份,
a
=
b
=
c
=
d
a=b=c=d
a=b=c=d
不是的话,保证
c
=
d
=
1
c=d=1
c=d=1,那么
l
c
m
(
c
,
d
)
=
1
lcm(c,d)=1
lcm(c,d)=1,剩下保证gcd为1就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,a,b,c,d;
void solve(){
scanf("%d",&n);
if(n%4==0){printf("%d %d %d %d\n",n/4,n/4,n/4,n/4);return;}
c=d=1; n-=2;
if(n%2==1) a=n/2,b=(n+1)/2;
else a=n/2-1,b=n/2+1;
printf("%d %d %d %d\n",a,b,c,d);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
B. Array Cloning Technique
题意:
给定长度为n的序列,最少进行多少次操作是使得有一个序列的元素全部相等。
一次操作:
①复制任意一个序列
②交换任意两个序列中的一个元素
思路:
肯定是去使初始最多的元素全部填满某个序列,如果总数不够,就去复制一次。
然后将复制出来的全部移到原序列。还不够就再复制,然后再交换,直到填满为止
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n,m,q;
map<int,int>mp;
void solve(){
mp.clear();
scanf("%d",&n);
int mx=0;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
mp[x]++; mx=max(mx,mp[x]);
}
int ans=0;
while(mx<n){
ans++;
ans+=min(mx,n-mx);
mx*=2;
}
printf("%d\n",ans);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
C. Tree Infection
题意:
一棵树。每秒钟每个有儿子节点感染的节点i,可以感染节点i的一个其他健康儿子节点,同时可以指定一个健康节点感染。问最少多少时间,全部节点感染?
思路:
很容易想到,节点之间有关系只能是同属于一个节点的儿子,只有这样a感染了才能去感染b,不然b就只能指定感染,耗费时间。
那么我们可以将全部节点分成一些集合,划分条件为同一个节点的儿子节点。
然后秉承
“
难
事
”
先
做
,
“
易
事
”
后
做
“难事”先做,“易事”后做
“难事”先做,“易事”后做的原则,我们初始一次在指定感染的时候,按照集从大到小的顺序感染,这样每感染后一个节点时,我们可以之前的可以传递感染,这样在每个集合都有感染后,原来大集合已经感染的也最多,这样是最优的。
剩下的同样在这张规则下,继续指定感染。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n,p[N],t1,t2;
vector<int>edge[N],num;
void solve(){
t1=t2=0; num.clear();
scanf("%d",&n);
edge[0].push_back(1);
for(int i=2;i<=n;i++){
int x; scanf("%d",&x);
edge[x].push_back(i);
}
for(int i=0;i<=n;i++){
int len=edge[i].size();
if(len!=0) num.push_back(len);
}
sort(num.begin(),num.end());
t1=num.size();
priority_queue<int>q;
for(int i=0;i<t1;i++) q.push(num[i]-(i+1));
while(1){
if(q.empty()) break;
auto u=q.top(); q.pop();
if(u<=0) continue;
if(u<=t2) break;
q.push(u-1); //指定感染
t2++;
}
printf("%d\n",t1+t2);
for(int i=0;i<=n;i++) edge[i].clear();
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
D. GCD Guess
题意:
每个询问a,b ,得到输出gcd(a+x,b+x) ,最多询问30次,问x的值是多少,x的范围(1<=x<=1e9)
思路:
这题挺好想的吧,1e9正好只有30位,所以我们一位位去判断是否可行就好了。
我们判断第i为的0/1情况时,我们是得到
a
+
x
=
(
1
<
<
i
)
a+x=(1<<i)
a+x=(1<<i),同时
b
+
x
b+x
b+x的第i位也是1,但是要比(1<<i)大。
这样如果
g
c
d
!
=
(
1
<
<
i
)
gcd!=(1<<i)
gcd!=(1<<i),那么第i位取1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5;
int n;
int query(int a,int b){
cout<<"? "<<a<<" "<<b<<endl;
int gd;
cin>>gd;
return gd;
}
void print(int x){
cout<<"! "<<x<<endl;
}
void solve(){
int ans=0;
for(int bit=0;bit<30;bit++){
int ned1=(1<<bit);
int ned2=(1<<bit)+(1<<30);
int gd=query(ned1-ans,ned2-ans);
if(gd!=ned1) ans+=ned1;
}
print(ans);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}