本文主要是介绍Codeforces Round #671 (Div. 2)A-E题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Codeforces Round #671 (Div. 2)A-E题解
//写于rating值1987/2184
//这场的A-D是真的水…
比赛链接:https://codeforces.ml/contest
A题
水题
题意为给定一个长度为n的数字,两个人交替对这个数字上的某一位进行标记。先手的人只能标记奇数位上的数,后手的人只能标记偶数位上的数。当只剩下一个位置上的数没被标记时,如果这个数字是奇数那么先手胜利,如果这个数是偶数,那么后手胜利。
注意到这道题里面,先手后手两个人能标记的数字部分是完全互不相关的。最后一个剩下的数字到底是奇数位置还是偶数位置上的数,只由n的奇偶性决定。如果最后剩下的是奇数位上的数字,先手的人肯定希望自己能胜利,因此如果奇数位上的数字如果有任意一个是奇数的话,他只要把这个数留到最后就能保证自己胜利了。
最后剩下的是偶数位上的数字的时候同理。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;int32_t main()
{IOS;int t;cin>>t;while(t--){int n;cin>>n;string s;cin>>s;bool flag;if(n&1)//如果总数量是奇数的话,奇数位上的数比偶数位上多1个,最后剩下的那个数一定是奇数位上的数{flag=0;for(int i=0;i<n;i+=2)//只要奇数位上的数存在任何一个是奇数,先手的人都可以使得它成为最后一个数,使得自己获胜if(s[i]%2) flag=1;}else//对应总数量是偶数的情况,最后剩下的数一定是偶数位上的数{flag=1;for(int i=1;i<n;i+=2)//只要偶数位上的数存在任何一个是偶数,后手的人都可以使得它成为最后一个数,使得自己获胜if(s[i]%2==0) flag=0;}if(flag) cout<<1<<endl;else cout<<2<<endl;}
}
B题
简单规律总结
题意为给定x个正方形小方块,要你尽可能构造多的不相同的完美台阶。n阶完美台阶的定义为,从左到右各列的方块数依次为1-n个,并且这个台阶恰好能分成n个正方形部分。
其实容易想到,这样的一个完美台阶必然是一个轴对称的图形,通过画图可以辅助理解得到结论。
首先只有1层的台阶是完美的,而2层的台阶是不完美的。
3层的台阶左下和右上可用1层完美台阶去补,剩下的部分恰好是个正方形。
7层的台阶左下和右上可用3层完美台阶去补,剩下的部分恰好是个正方形。
…
可以推得结论,完美台阶的层数从小到大的递推公式就是f(n+1)=f(n) × \times ×f(n)+1
样例的最后一个数据已经给了最大的x取值1e18时候答案也只有30,因此直接暴力循环即可。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;ll num[70];int32_t main()
{IOS;int t;cin>>t;while(t--){ll x;cin>>x;ll ans=0,cas=1;while(x>0){x-=(1+cas)*cas/2;if(x>=0) ans++;cas=cas*2+1;}cout<<ans<<endl;}
}
C题
贪心,分类讨论
题意为现在有一种病毒在codeforces网站上传播,它会在rating值相等的人之间进行传播。一开始的时候只有一个人被感染了,他的rating值为x,并且他无法参加比赛(无法改变自己的rating值)。现在有n个人各自有一个初始的rating值,每次参加比赛你可以任意改变他们的rating值,但是所有人的rating变化值的总和必须为0。现在询问做少需要几次比赛,可以让所有人都感染上病毒。
首先我们按照初始rating值和x相等的人数的情况进行分类,设初始rating值和x相等的人为cas。
如果cas=n,那么代表所有人一开始就都被感染了,直接输出0。
如果cas<n但是cas>0,代表一开始有一部分人被感染了,还有一部分未被感染。我们可以通过1次比赛,让为被感染的人的rating值全部变为x,总改变值为0,那么对应的与之相反的该变量直接加到那些一开始就被感染的人身上即可。因此此时直接输出1。
如果cas=0的话,代表一开始所有人都没有被感染,那就不能采取上一种情况的策略了。
此时我们计数一下n个人的rating值总和sum,如果sum能够整除n,并且sum/n=x的话,那么我们可以用通过1次比赛就把所有人的rating值变为x。
而如果sum/n!=x或者sum无法整除n的时候,我们必然不可能在1次比赛内使得所有人感染,但是我们可以1次比赛制造出1个感染者,然后采取上面0<cas<n的策略,就可以总共在2次比赛就使得所有人感染。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;int32_t main()
{IOS;int t;cin>>t;while(t--){int n,x;cin>>n>>x;int sum=0,cas=0;for(int i=0;i<n;i++){int temp;cin>>temp;if(temp!=x) sum+=temp;else cas++;}if(cas==n) cout<<0<<endl;else if(cas) cout<<1<<endl;else{if(sum%n==0&&sum/n==x) cout<<1<<endl;else cout<<2<<endl;}}
}
D1+D2题
贪心,构造
题意为给你n个数字,你需要把他们进行重新打乱排列。使得其中满足比自己左侧和右侧相邻数都小的数字尽可能多(下标1和下标n的数字不算,也就是开头和末尾的数字都不算),输出最大的满足的数字数量,并输出任意一种满足的构造方式。
这里我们采取贪心的策略,对奇数下标构造小的数字(下标从0开始),偶数下标构造大的数字。
这是因为左右边界的数字是不算的,也就是下标0是必然不算的,我们最多可以从下标1开始构造满足的数。而如果x[1]<x[2]的话,那x[2]就必然不是满足的数了。也就是我们最优也必须隔着一个数字构造满足的数字。也就是奇数下标要构造相对小的数字,偶数下标要构造相对大的数字。
直接对原有数字排序后,按照从小到大,下标从左到右依次放到奇数下标,再下标从左到右依次放到偶数下标。这样我们就保证了1.偶数下标的是最大的那些数,并且2.偶数下标中的最小值也不小于奇数下标中的最大值。我们这样排列,也保证了3.偶数下标中相对小的数会和奇数下标中相对小的数相邻。
根据上面三条可以推出这样的贪心是最优的策略。
D1和D2的差别就在于D1的数字是各不相同的,所以满足条件的数字个数就是(n-1)/2,而D2的数字是可能相同的,我们需要再for一遍去检测一下具体有解满足条件的数字而已。构造方法是完全相同的。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;vector<ll>num;
vector<ll>out;
ll n;int32_t main()
{IOS;cin>>n;num.resize(n);out.resize(n);for(auto &x:num) cin>>x;sort(num.begin(),num.end());ll tar=0;for(ll i=1;i<n;i+=2) out[i]=num[tar++];for(ll i=0;i<n;i+=2) out[i]=num[tar++];ll ans=0;for(ll i=1;i<n;i+=2) if(i+1<n&&out[i-1]>out[i]&&out[i+1]>out[i]) ans++;cout<<ans<<endl;for(ll i=0;i<n;i++){if(i) cout<<' ';cout<<out[i];}cout<<endl;
}
E题
数论,复杂度分析,构造
题意为给定一个数字n,要求你对n除1外的所有因数排列成一个环,要求相邻两个位置之间互质的对数最少。输出一种构造方案,并输出最少的互质对数。
这里的话容易想到,n的所有因子,都可以由n的质因子的不同幂次组合来得到。然后我们注意到,对于两个质因子a和b。如果我们在a和b中间放入了一个既能整除a又能整除b的数c的话,变成a,c,b。那么在a和c之间就可以放所有能整除a的因子,c和b之间就可以放所有能整除b的因子。
由此我们可以先找出n的所有不同的质因子,然后在他们两两之间放入一个能整除相邻两个质因子的n的因子,接着其他所有的数能必定至少能被这些质因子的某一个整除,也就是能按照上段所述插入到间隔里。
接着就是想这种方案是否永远可行。
对于这些质因子来说,两个质因子的积必定也是n的因子,并且不同的质因子之间得到的积必然是不相等的,因此我们可以在相邻质因子之间插入他们的积的构造方案。但是这种方案,在n=a × \times ×b的时候是不成立的,因为此时n只有三个因子,a,b和n,如果按照上述方案会构造成a,n,b,n。重复使用了两次n。此时我们实际上只能构造a,n,b,这样会存在1对相邻互质的数,此时我们直接输出a,n,b,endl,1,endl即可。因此我们特判一下n恰好等于a × \times ×b的情况。
但是这样会wa在第十个点。因为如果只有两个质因子的话,虽然我们可以构造0对相邻互质的数的情况,但是我们同样会使用两次a × \times ×b。
这时候就要我们分析复杂度了,因为这里题意说了所有n的因子数量是不超过2e5的,根据因子是由质因子不同的幂次组合而成的,容易得到质因子的个数是一个很小的值,不会超过1e3,因此我们可以采取比较暴力的方案。
排除掉n=a × \times ×b这种必须构造1对相邻互质数的情况,其他情况下,相邻质因子之间的那个能整除他们的数的选择,可以直接暴力去n的所有因子里去找。
以上。
#include<bits/stdc++.h>
#define ll long long
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const ll maxn=31630;vector<ll>prime;//线性筛筛出sqrt(1e9)内的所有素数
bool v[maxn];void primes()
{for(ll i=2;i<maxn;i++){if(!v[i]){v[i]=1;prime.push_back(i);}for(ll j=0;prime[j]<maxn/i;j++){v[prime[j]*i]=1;if(i%prime[j]==0) break;}}
}int32_t main()
{IOS;primes();int t;cin>>t;while(t--){ll n;cin>>n;vector<ll>primes_now;//保存当前的n分解质因数后有几个不同的质因子ll temp=n;for(ll i=0;i<prime.size()&&prime[i]<=temp/prime[i];i++){if(temp%prime[i]==0){primes_now.push_back(prime[i]);while(temp%prime[i]==0) temp/=prime[i];}}if(temp>1) primes_now.push_back(temp);unordered_map<ll,bool>M;//保存n除了1外的因子有哪些,记录它是否被使用过for(ll i=2;i<=n/i;i++){if(n%i==0){M[i]=0;M[n/i]=0;}}M[n]=0;if(primes_now.size()==2&&primes_now[0]*primes_now[1]==n) cout<<primes_now[0]<<' '<<primes_now[1]<<' '<<n<<endl<<1<<endl;//如果n刚好能拆分成两个质因子相乘,就是唯一一种特殊的情况,我们无法构造出0对相邻互质而只能构成1对相邻互质的特殊情况,直接输出三个数字和对数1。else{vector<ll>mid(primes_now.size());//mid[i]当前n分解的质因子,第i个和第i+1个中间插入的数是哪一个for(ll i=0;i<primes_now.size();i++)//直接暴力for一遍,质因子数量是很少的{for(auto &x:M)//去n的所有因数里面找既能整除第i个质因子又能整除第i+1个质因子的数{if(!x.second&&x.first%primes_now[i]==0&&x.first&&x.first%primes_now[(i+1)%primes_now.size()]==0){mid[i]=x.first;x.second=1;break;}}M[primes_now[i]]=1;}for(ll i=0;i<primes_now.size();i++)//然后把剩下没使用过的数字继续暴力放到第i个质因子和mid[i]中间,只需要满足整除第i个质因子即可{cout<<primes_now[i]<<' ';for(auto &x:M){if(!x.second&&x.first%primes_now[i]==0){cout<<x.first<<' ';x.second=1;}}cout<<mid[i]<<' ';}cout<<endl<<0<<endl;}}
}
这篇关于Codeforces Round #671 (Div. 2)A-E题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!