目录
B. Array Cloning Technique (模拟)
A. GCD vs LCM(思维)
题意:给你一个数n,把n分解成a,b,c,d四个数,a和b的最大公因数要等与b和d的最小公倍数,且a+b+c+d=n。
思路:我们知道1和任意正整数的最大公因数都是1,所以把n分一个1出来当最大公因数,最大公倍数那边也分两个1出来,这样总是满足要求的。
原题链接:https://codeforces.com/contest/1665/problem/A
AC代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
int main(){
IOS;
int t;
cin >> t;
int n;
while(t--){
cin >> n;
cout << n-3 << " 1 1 1" << endl;
}
return 0;
}
B. Array Cloning Technique (模拟)
题意:有一个n元素的数组,每次你可以选择一个操作,把一个数组赋值一份或者交换任意两个数组中的一个元素。问有一个数组内元素全部相同所需要的最小操作次数。
思路:选择所给数组中最多的数进行交换,每次数组中这个元素会多一倍,每次把交换后的数组复制。
原题链接:https://codeforces.com/contest/1665/problem/B
AC代码:
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
int main(){
IOS;
int t;
cin >> t;
int n;
map<int,int> ma; //map记录数组各个数的个数
while(t--){
cin >> n;
ma.clear(); //每次清空
int ni=0; //ni表示个数最多的数
int x;
for(int i=0;i<n;i++){
cin >> x;
ma[x]++;
if(ma[x]>ma[ni]) ni=x; //更新个数最多的数
}
int sum=ma[ni]; //最初数组中最多的元素ni的个数
int ans=0; //操作数
while(sum<n){ //数组中相同的个数小于n个
ans++; //复制数组
if(sum*2>n) ans+=n-sum; //超出n个数的时候
else ans+=sum; //没有超出n个数
sum*=2; //每次数组中这个数ni多一倍
}
cout << ans << endl;
}
return 0;
}
C. Tree Infection(二分)
题意:一棵树,每一秒可以进行两个操作,注射任意一个节点使这个节点感染或者选择已经感染的一个节点传染给一个兄弟节点(有相同的父亲)。
思路:既然可以传染给兄弟节点,那么肯定要先注射兄弟节点多的点,让它们内部自己传染,其它的不能通过传染来感染的点只能用注射来感染。
原题链接:https://codeforces.com/contest/1665/problem/C
AC代码:
#include<iostream>
#include<algorithm>
#include<queue>
#include<utility>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define endl '\n'
using namespace std;
typedef pair<int,int> pii;
const int maxn=2e5+10;
int vis[maxn],child[maxn]; //vis记录当前点是否使用过,chird表示子节点的个数
int main(){
IOS;
int t;
cin >> t;
int n;
while(t--){
cin >> n;
for(int i=1;i<=n;++i) child[i]=0;
int x;
for(int i=1;i<n;++i){
cin >> x;
child[x]++; //x的子节点个数加1
}
int l=1,r=n,ans=n; //二分次数(答案)
while(l<=r){
int mid=(l+r)>>1;
for(int i=1;i<=n;++i) vis[i]=0;
priority_queue<pii> q; //创建最大堆
for(int i=1;i<=n;++i){
pii nx(child[i],i);
if(child[i]) q.push(nx); //这个点有子节点,入堆
}
pii nx(1,0); //顶点也要入堆
q.push(nx);
for(int i=1;i<=mid;++i){ //遍历当前次数能不能感染全部结点
if(q.empty()) break;
pii now=q.top();q.pop(); //每次取节点个数最多的那个
if(!vis[now.second]){ //如果这个节点没被感染
now.first-=(mid-i+1); //剩下的时间能感染的节点个数
vis[now.second]=1;
}
else now.first--; //感染过则传播给相同父节点的子节点
if(now.first>0) q.push(now); //没感染完则再次加入堆中
}
if(q.empty()){ //如果当前次数能全部感染完,寻找更小的次数
ans=mid;
r=mid-1;
}
else l=mid+1; //否则不能感染完,寻找更大的次数
}
cout << ans << endl;
}
return 0;
}
D. GCD Guess
题意:最多询问30次机会,猜测一个数x,每次询问给出a和b,返回gcd(x+a,x+b)。
思路:去判断x每一位的二进制是0还是1,通过回答来确定x二进制上每一位的值,我们每次可以记录0到i位的答案为ansi,查询gcd(x−ansi+2^(i+1),x−ansi+2^(i+1)+2^(i+2))=gcd(x−ansi+2^(i+1)+2^(i+2))=gcd(x−ansi+2^(i+1),2^(i+2)) 来确定第i+1位的值,如果得到的回答不等于2^(i+2) ,那么第i+1位就为1,否则为0。
原题链接:https://codeforces.com/contest/1665/problem/D
AC代码:
#include<iostream>
#define endl '\n'
using namespace std;
void solve(){
int ans=0;
for(int i=0;i<30;i++){
int a=(1<<i)-ans;
int b=(1<<i+1)+(1<<i)-ans;
cout << "? " << a << " " << b << endl;
int temp;
cin >> temp;
if(temp!=(1<<i)) ans+=(1<<i);
}
cout << "! " << ans << endl;
}
int main(){
int t;
cin >> t;
while(t--){
solve();
}
return 0;
}