Codeforces Round #781 (Div. 2)(ABCDE)
Posted 斗奋力努
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #781 (Div. 2)(ABCDE)相关的知识,希望对你有一定的参考价值。
Codeforces Round #781 (Div. 2)(ABCDE)
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。因为如果第i位为1的话,再加(1<<i),会使得第i位为0,这样gcd就肯定不会为(1<<i)
#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++) //判断bit位
int ned1=(1<<bit);
int ned2=(1<<bit)+(1<<30);
int gd=query(ned1-ans,ned2-ans); //将[0,max(0,bit-1)]位的值减去
if(gd!=ned1) ans+=ned1;
print(ans);
int main()
int T;scanf("%d",&T);
while(T--) solve();
return 0;
E. MinimizOR
题意:
有一个长度为n的序列,没有修改操作,q次查询。
每次去查询区间[l,r],输出其中不同位置两个元素的最小或值
i
!
=
j
&
&
m
i
n
(
a
[
i
]
∣
a
[
j
]
)
i!=j\\&\\&min(a[i]|a[j])
i!=j&&min(a[i]∣a[j])
思路:
(神仙题)
我们知道或运算得到数值是看在二进制下某一位是否有1出现,有才会对结果做出贡献。
那么现在我们知道数据范围
(
0
≤
a
[
i
]
<
2
30
)
(0≤a[i]<2^30)
(0≤a[i]<230),从高位往低位看,我们尽量保证选择的两个元素的高位没有1,等价于1尽可能的出现在低位。直接暴力判断肯定不好使,不过我们要让1尽可能出现在低位,也就意味着我们选择的数尽可能的小。
那么每次查询的时候,我们就找区间中最小的
m
i
n
(
r
−
l
+
1
,
31
)
min(r-l+1,31)
min(r−l+1,31)个数,然后两两组队,暴力判断最小或值。
这里可以用线段树维护,每次维护的区间记录区间最小值和最小值位置。每找到一个就记录一个,同时将其位置修改乘最大值,这样在这次之后的查询时不会重复利用,记得最后要修改回来。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>PII;
const int N=1e5+5;
const int inf=INT_MAX;
int n,q,a[N];
PII b[35];
struct node
int l,r;
PII mi_pos; //mi_pos.first=min, mi_pos.second=pos
tr[N<<2];
void pushup(int u)tr[u].mi_pos=min(tr[u<<1].mi_pos,tr[u<<1|1].mi_pos);
void build(int u,int l,int r)
tr[u]=l,r,inf,0;
if(l==r)
tr[u].mi_pos=a[l],l;
return;
int mid=(l+r)>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
PII query(int u,int l,int r)
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].mi_pos;
PII ans=inf,0;
int mid=(tr[u].l+tr[u].r)>>1;
if(l<=mid) ans=min(ans,query(u<<1,l,r));
if(r>mid) ans=min(ans,query(u<<1|1,l,r));
return ans;
void modify(int u,int x,int mi,int pos)
if(tr[u].l==x&&tr[u].r==x)
tr[u].mi_pos=mi,pos;
return;
int mid=(tr[u].l+tr[u].r)>>1;
if(x<=mid) modify(u<<1,x,mi,pos);
if(x>mid) modify(u<<1|1,x,mi,pos);
以上是关于Codeforces Round #781 (Div. 2)(ABCDE)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #781 (Div. 2)(ABCDE)
Codeforces Round #436 E. Fire(背包dp+输出路径)
[ACM]Codeforces Round #534 (Div. 2)
Codeforces 781C Underground Lab 构造