众所周知,codeforce div2AB两题以思维题为主,极少涉及算法知识。对于新手来说,能够快速并准确地解决B题是上分的关键。当AB题的写题量达到一定程度时,可以发现所谓思维题也有套路可循。据此,笔者将套路归纳为几大块,并收纳了一些具有一定代表性的题目。
本题单持续更新。
模拟
这里放上一些对模拟码力要求较高而解题性质较明显的题目。
732(div2) A.AquaMoon and Two Arrays
题意:虽然是A题,但还是有点码力要求的 给两个序列,可以对第一个序列进行任意次操作,每次操作可选取两个数,使其中一个数加一,另一个数减一,问能否进行小于等于100次的操作,使第一个序列变成第二个序列,并将操作过程输出。
思路:纯模拟+贪心。将所有数分为两部分,要加的和要减的,把要加的差减去要减的,可以发现最后两差分数组所有数必为零,不为零输出-1。
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
struct Node{
int x,y;
};
bool cmp(Node x,Node y){
return x.y < y.y;
}
signed main(){
IOS
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1],b[n+1];
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++) cin>>b[i];
Node d1[n+1],d2[n+1];
//d1存要加的,d2存要减的
//处理d1 d2数组 y存差值 x存下标
int cnt = 0,snt = 0;
for (int i=1;i<=n;i++){
int d = a[i] - b[i];
if (d<0){
cnt++;
d1[cnt].y = -d;
d1[cnt].x = i;
}
else if (d>0){
snt++;
d2[snt].y = d;
d2[snt].x = i;
}
}
//贪心 根据值排序
sort(d1+1,d1+cnt+1,cmp);
sort(d2+1,d2+snt+1,cmp);
vector<pair<int,int>>op;
//op存操作过程
bool flag = 0;
//flag判断能否成功变成
//模拟部分
for (int i=1;i<=cnt;i++){
int dd = d1[i].y;
//取出一个d1 然后暴力遍历d2
for (int j=1;j<=snt;j++){
if (d2[j].y>=dd){
//d2 比 dd大 直接减
d2[j].y -= dd;//d2减去dd贡献
for (int k=1;k<=dd;k++){
op.push_back({
d2[j].x,d1[i].x});
}//存操作数
dd = 0;//dd清零
break;
}
else {
for (int k=1;k<=d2[j].y;k++){
op.push_back({
d2[j].x,d1[i].x});
}
dd -= d2[j].y;//dd减去贡献
d2[j].y = 0;//d2变为0
}
}
if (dd){
//d1经过遍历d2后都不能变成0 说明无法变成
flag = 1;
break;
}
}
//如果可以变成第二个序列,d2数组在操作后必为零
for (int i=1;i<=snt;i++){
if (d2[i].y){
flag = 1;//不为零就说明变成不了
break;
}
}
//输出部分
if (flag) cout<<-1<<endl;
else {
cout<<op.size()<<endl;
for (auto it:op){
cout<<it.first<<" "<<it.second<<endl;
}
}
}
}
751(div2) B. Divine Array
题意:给一串数,进行无数次操作,每次操作让每个数变成它出现的次数。进行q次询问,询问第x下标第k***作的值
思路:模拟+思维。对于无数次操作的题目,显然是去找一个“不动结点”,即发现哪次操作后数组值不会改变。手模一遍后发现,当每个数的数值等于它出现次数时,它就不变了。故用桶模拟出这个过程即可。
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
signed main(){
IOS
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];
map<int,int>mp;
int m[n+1][n+1];
//m[i][k]第k次操作第i个数的值
//mp记录每个数出现次数
for (int i=1;i<=n;i++){
cin>>a[i];
m[i][0] = a[i];
mp[a[i]]++;
}
int cnt = 0;
while (1){
bool flag = 0;
cnt++;
//cout<<"cnt = "<<cnt<<endl;
for (int i=1;i<=n;i++){
m[i][cnt] = mp[m[i][cnt-1]];
//cout<<m[i][cnt]<<" ";
//mp[mmp[a[i]]]++;
}
mp.clear();
for (int i=1;i<=n;i++){
mp[m[i][cnt]]++;
}
for (int i=1;i<=n;i++){
if (mp[m[i][cnt]]!=m[i][cnt]) flag = 1;
}
//cout<<endl;
if (flag) continue;
else break;//当所有数等于出现次数时 不会再改变 跳出循环
}
int q;cin>>q;
while (q--){
int x,k;cin>>x>>k;
if (k>cnt) k = cnt;
cout<<m[x][k]<<endl;
}
//cout<<"......."<<endl;
}
}
705(div2) B. Planet Lapituletti
题意:自定义小时进制h,分钟进制m,给出当前时间,求最近的在镜子里合法的未来时间。
思路:模拟+简单规律。可以发现镜子里的合法时间仅包括0,1,2,5,8五个数字,故暴力枚举所有合法时间和其镜子里的映射时间,判断两者是否均符合进制,记录离当前时间最近的时间即可。
#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
using namespace std;
char s[] = {
'0','1','2','5','8'};
string s1[30];
int cnt;
int at(string s){
//计算现实时间
int ans = 0;
for (int i=0;i<s.size();i++)
ans = ans * 10 + s[i] - '0';
return ans;
}
int bt(string s){
//映射为镜子里的时间
int ans = 0;
for (int i=s.size()-1;i>=0;i--)
if (s[i]=='2') ans = ans * 10 + '5' - '0';
else if (s[i]=='5') ans = ans * 10 + '2' - '0';
//注意2和5需要转换
else ans = ans * 10 + s[i] - '0';
return ans;
}
int main(){
for (int i=0;i<5;i++){
for (int j=0;j<5;j++){
string ss;ss += s[i];
ss += s[j];
s1[cnt++] = ss,ss.clear();
}
}//预处理所有合法时间
int t;cin>>t;
while (t--){
int h,m;cin>>h>>m;
int a,b;
scanf("%d:%d",&a,&b);
int mini = INF;
string ans_h,ans_m;
for (int i=0;i<cnt;i++){
//暴力枚举所有合法时间
for (int j=0;j<cnt;j++){
int hi = at(s1[i]),mi = at(s1[j]);
int hh = bt(s1[j]),mm = bt(s1[i]);
//hi,mi存现实时间
//hh,mm存镜子时间 注意小时和分钟互换
if ((hi>=h)||(mi>=m)||(mm>=m)||(hh>=h)) continue;
//判断是否符合要求的进制
int ti = (hi * m + mi) - (a * m + b);
//ti计算同一天的时间差
if (ti<mini&&ti>=0){
mini = ti;
ans_h = s1[i],ans_m = s1[j];
}
int si = hi * m + mi + h * m - (a * m + b);
//si计算不同天的时间差
if (abs(si)<mini){
mini = abs(si);
ans_h = s1[i],ans_m= s1[j];
}
}
}
cout<<ans_h<<":"<<ans_m<<endl;
}
}
748(div3) B. Make it Divisible by 25
题意:给一串数,去掉任意数后使其变为25的倍数,求最少去掉几个数
做法:数学规律+暴力枚举。发现规律25的倍数末尾必为00,25,50,75,故暴力枚举、用双指针往后找越接近末尾的子序列,去掉的数=右指针到末尾的数+左右指针中间的数,再取最小值即可
#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
#define int long long
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
using namespace std;
string s[] = {
"00","25","50","75"};
signed main(){
IOS
//for (int i=25;i<=100000;i+=25) cout<<i<<endl;
int t;cin>>t;
while (t--){
string ss;cin>>ss;
int len = ss.length();
int ans = INF;
for (int i=0;i<4;i++){
int l = -1,r = -1;
for (int k=0;k<ss.size();k++){
if (ss[k]==s[i][1]){
r = k;
}
}//右指针先找末尾数的位置
if (r!=-1){
for (int k=0;k<ss.size()&&k<r;k++){
if (ss[k]==s[i][0]){
l = k;
}
}
}
//cout<<"l = "<<l<<" r = "<<r<<endl;
if (l!=-1&&r!=-1) {
ans = min(len-r-1+r-l-1,ans);
}
}
cout<<ans<<endl;
}
}
753(div3) B. Odd Grasshopper
题意:给一个原坐标x0,可以向左或向右跳t格(t为时间点,如果第一次跳就跳1格,第二次跳就跳2格,以此类推),如果在奇数坐标向右跳,在偶数就向左跳,问跳了n次后的坐标位置。
思路:思维+模拟。很有趣的题目。纸上画一下可发现跳4次是一个循环节,也可打表找规律,再分类讨论一下原坐标x0的奇偶。
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int t;cin>>t;
while (t--){
int x,n;cin>>x>>n;
int ans = x;
if (x%2==0){
if (n%4==0) ans = x;
else if (n%4==1) ans -= n;
else if (n%4==2) ans += 1;
else if (n%4==3) ans += n + 1;
}
else {
if (n%4==0) ans = x;
else if (n%4==1) ans += n;
else if (n%4==2) ans -= 1;
else if (n%4==3) ans -= n + 1;
}
cout<<ans<<endl;
}
}
贪心
常见套路是排序+堆
711(div2) B. Box Fitting
题意:一辆宽度为w的二维货车,有n个物品,每个物品有一定宽度,问物品最少能垒成几行
思路:动态贪心。易知先放大物品可使决策更优,故先将物品从大到小排序;每放一个物品,挑选剩下长度最长的行,故用大顶堆维护每行剩余长度。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n,w;cin>>n>>w;
int a[n+1];
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n,greater<int>());
priority_queue<int>q;
for (int i=1;i<=n;i++){
if (q.empty()) q.push(w-a[i]);
else {
if (q.top()<a[i]) q.push(w-a[i]);
else {
int t = q.top();q.pop();
t -= a[i];
q.push(t);
}
}
}
cout<<q.size()<<endl;
}
}
707(div2) B. Napoleon Cake
题意:一块蛋糕有n层,每层有a[i]奶油,奶油会往下渗,有多少奶油就向下渗几层,被渗透的层数用1表示,没被渗透的用0表示。
思路:贪心+模拟。因为蛋糕一定是从上往下渗透,故从后往前遍历,用cnt记录最多可以渗透几层。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1],b[n+1];
for (int i=1;i<=n;i++) cin>>a[i];
int cnt = a[n];
for (int i=n;i>=1;i--){
if (a[i]){
//如果当前层数有奶油 考虑更新cnt
if (cnt<a[i]){
cnt = a[i] - 1;
b[i] = 1;
}
else {
cnt--;
b[i] = 1;
}
}
else {
//如果没有奶油 直接看cnt状态
if (cnt) b[i] = 1,cnt--;
else b[i] = 0;
}
}
for (int i=1;i<=n;i++) cout<<b[i]<<" ";
cout<<endl;
}
}
673(div2) B. Two Arrays
题意:把序列分成两部分,使这两部分加起来等于k的数对最小
思路:贪心+思维。把小于k一半的数放在一起,把大于k一半的放一起,如果等于k的一半则对半标记
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;
struct Node{
int x,c;
};
int main(){
int t;cin>>t;
while (t--){
int n,k;cin>>n>>k;
int flag = 0;
Node a[n+1];
for (int i=1;i<=n;i++){
cin>>a[i].x;
if (a[i].x*2<k) a[i].c = 1;
else if (a[i].x*2>k) a[i].c = 0;
else if (a[i].x*2==k){
if (flag) a[i].c = 1;
else a[i].c = 0;
flag = 1 - flag;
}
}
for (int i=1;i<=n;i++) cout<<a[i].c<<" ";
cout<<endl;
}
}
Global Round 11 B. Chess Cheater
题意:給一串LW的字符串,若W前面也有W则它的贡献为2,没有则贡献为2。现在可以进行k次操作,将L变为W,问操作后的最大贡献。
思路:贪心。若有区间1WLW,将这个L填满带来3贡献;若有区间2WLLW,填满带来5贡献,平均每个L带来2.5贡献,小于区间2每个L带来的贡献。故贪心先填满小区间贡献最大,将每个区间放进容器排序,再计算剩下的操作即可。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n,k;cin>>n>>k;
string s;cin>>s;
if (n==1&&k>=1){
cout<<1<<endl;
continue;
}
s = '0' + s;
int flag = 0,ans = 0,cnt = 0;
vector<int>v;
for (int i=1;i<=n;i++){
if (s[i]=='W'){
if (!flag) flag = i;
else v.push_back(i-flag-1),flag = i;
ans++;
cnt++;
}
}
sort(v.begin(),v.end());
for (int i=0;i<v.size();i++){
if (v[i]<=k){
k -= v[i];
ans += v[i] * 2 + 1;
cnt += v[i];
}
}
if (!cnt&&k) ans += (k - 1) * 2 + 1;
else if (k){
cnt = n - cnt;
ans += min(cnt,k) * 2;
}
cout<<ans<<endl;
}
}
位运算题
包括&,^,|等各种操作题
717(div2) B. AGAGA XOOORRR
题意:给一串序列,对其进行任意次操作,每次操作选取相邻的两个数进行异或,使其变成长度不小于2的每个数相等的序列。
思路:数学+思维。1.若长度为偶数的相等序列,它们的异或值必为0;2.若长度为奇数的相等序列,它们的异或值必为这些相等数。
于是可以倒推思考。异或值为0→一定可以变成相等序列;最后异或值不为0→若有异或值等于这个值的区间数大于等于2,则可以变成相等序列
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];
int ans = 0;
for (int i=1;i<=n;i++){
cin>>a[i];
ans ^= a[i];
}
//cout<<"ans = "<<ans<<endl;
if (!ans) puts("YES");
else {
int sum = 0,cnt = 0;
for (int i=1;i<n;i++){
sum ^= a[i];
if (ans==sum){
cnt++;
sum = 0;
}
}
if (cnt>=2) puts("YES");
else puts("NO");
}
}
}
Round 714(div2)B. AND Sequences
题意:给一串序列,问对任意i<=n-1满足a1&a2&…&ai=ai+1&ai+2&…&an 有多少种排列方式
思路:数学+思维。通过观察,可以发现a1和an是永远都在等式两边的,假若a1和an不相等,则等式无法成立。
简单证明:
以样例为例,当n = 5时,有:
s1=s2&s3&s4&s5
s1&s2&s3&s4=s5
两式联立有:
s1=s2&s3&s4&s1&s2&s3&s4=s2&s3&s4&s1=s5
(&运算符合交换律和结合律,两个相等的数&后是本身)
同时其值要等于所有数的&值,有:
s1=s2&s3&s4&s5 = 0
s1 = s1&s1=s2&s3&s4&s5&s1=0
s5同理
故挑选相同和等于所有数&值的数在两边,其余数任意排即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
const int N = 2e5 + 7;
int f[N];
signed main(){
int t;cin>>t;
f[0] = 1,f[1] = 1;
for (int i=2;i<=2e5;i++){
f[i] = i * f[i-1] % mod;
}//预处理阶乘
while (t--){
int n;cin>>n;
int a[n+1];
int ans = 0,snt;
map<int,int>mp;
for (int i=1;i<=n;i++){
cin>>a[i];
if (i==1) snt = a[i];
else snt &= a[i];
mp[a[i]]++;
}
if (mp[snt]>1){
int c = mp[snt] * (mp[snt]-1) % mod;
int d = n - 2;
ans = (ans + c*f[d]%mod) % mod;
}
cout<<ans<<endl;
}
}
752(div2) B. XOR Specia-LIS-t
题意:将一个序列分为任意段,判断能否使每一段最长上升子序列的长度异或为0
思路:思维+数学。最长上升子序列和异或的关系看上去好像很难联系在一起。这里我们把问题简单化。
绝大多数异或题必分奇偶。如果序列为偶数长度,直接全分为长度为1的小段,它们的异或和肯定为0;如果序列为奇数长度,只需找到相邻一段a[i]>=a[i+1]的区间,使它和其他长度为1的小段凑成偶数个数,如果找不到,那就无法实现。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];cin>>a[1];
bool flag = 0;
for (int i=2;i<=n;i++){
cin>>a[i];
if(a[i]<=a[i-1]){
flag = 1;
}
}
if (flag||n%2==0) puts("YES");
else puts("NO");
}
}
672(div. 2) B. Rock and Lever
题意:问一个序列中有多少对异或值等于与值 ai & aj≥ai⊕aj
思路:思维。可以发现二进制同位数的与值必大于异或值,不同位则异或大于与值。故用桶统计每位有多少个数即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
int bit_sum(int x){
int sum = 0;
while (x){
x >>= 1;
sum++;
}
return sum;
}
signed main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int cnt = 0;
unordered_map<int,int>mp;
set<int>s;
for (int i=1;i<=n;i++){
int x;cin>>x;
mp[bit_sum(x)]++;
s.insert(bit_sum(x));
}
for (auto it=s.begin();it!=s.end();it++){
if (mp[*it]>=2){
cnt += (mp[*it]-1) * (mp[*it]) / 2;
}
}
cout<<cnt<<endl;
}
}
构造
通常是构造出一个特殊结构使之满足题目要求。
720(div2) B. Nastia and a Good Array
题意:有一串正数序列,进行不大于n次操作,每次操作任选两个数x,y满足min(a[i],a[j]) = min(x,y),将a[i]替换为x,a[j]替换为y,使这个序列相邻两个数的gcd都等于1
思路:构造+思维。找到最小数,将它两边的数都替换为以它为起点的等差为1的递增序列即可
#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
#define int long long
#define x first
#define y second
using namespace std;
signed main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];
int mini = INF,mint = 0;
for (int i=1;i<=n;i++){
cin>>a[i];
if (a[i]<mini){
mini = min(a[i],mini);
mint = i;
}
}
cout<<n-1<<endl;
int cnt = mini;
for (int i=mint-1;i>=1;i--){
cout<<mint<<" "<<i<<" "<<mini<<" "<<++cnt<<endl;
}
cnt = mini;
for (int i=mint+1;i<=n;i++){
cout<<mint<<" "<<i<<" "<<mini<<" "<<++cnt<<endl;
}
}
}
Round 106(div2) B. Binary Removals
题意:将一串二进制序列删除几个不相邻的数使之变为有序的
思路:思维。可以发现序列中只要有1100的结构,必不可能有序,即判断有连续的1时,判断后面是否有连续的0即可(注意连续0不和连续1相邻同样不能有序)
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
string s;cin>>s;
bool flag = 0;
for (int i=0;i<s.size()-1;i++){
if (s[i]=='1'&&s[i+1]=='1'){
for (int j=i+2;j<s.size()-1;j++){
if (s[j]=='0'&&s[j+1]=='0'){
flag = 1;
break;
}
}
if (flag) break;
}
}
if (flag) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
}
Round 109(div2) B. Permutation Sort
题意:对一个n全排列进行任意次操作,每次操作能选择一段连续但不能等于n的区间,让区间的数任意排序,问最少能进行几次操作使全排列变成递增序列
思路:思维+构造。观察样例发现 如果最小值在最左边或最大值在最右边,那么只要选择其余一段的序列进行一次操作;如果最小值和最大值的位置互换,则必须进行3次操作;除此之外需进行2次
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];
bool flag = 0;
for (int i=1;i<=n;i++){
cin>>a[i];
if (i!=a[i]) flag = 1;
}
if (!flag) cout<<0<<endl;
else {
if (1==a[n]&&n==a[1]) cout<<3<<endl;
else if (n==a[n]||1==a[1]) cout<<1<<endl;
else cout<<2<<endl;
}
}
}
数学
涉及gcd,mex,max,整除,取模,奇偶,几何等知识
706(div2) B. Max and Mex
题意:对一个集合进行k次操作,每次操作加入(mex+max)/2向上取整的值,问去重后的集合大小
mex:没有出现在序列中的最小非负整数
思路:思维+数学。可以发现mex在操作过程中是一定不会出现在集合里的,即mex是定值。故进行分类讨论找到mex即可。
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int t;cin>>t;
while (t--){
int n,k;cin>>n>>k;
vector<int>v;
unordered_map<int,int>mp;
for (int i=1;i<=n;i++){
int x;cin>>x;
v.push_back(x);
mp[x]++;
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int si = v.size();
if (mp[0]){
int mexi = -1;
int maxi = v[si-1];
for (int i=1;i<si;i++){
if (v[i]-v[i-1]>=2){
mexi = v[i] + 1;
break;
}
}
if (mexi==-1) cout<<si+k<<endl;
else {
if (mp[(maxi+mexi+1)/2]) cout<<si<<endl;
else {
if (k) cout<<si+1<<endl;
else cout<<si<<endl;
}
}
}
else {
if (mp[(v[si-1]+1)/2]) cout<<si<<endl;
else if (k) cout<<si+1<<endl;
else cout<<si<<endl;
}
}
}
708(div2) B. M-arrays
题意:问序列能被分成几个子序列,使每个子序列相邻两个数的和能被m整除
思路:思维+数学。若两个数取模后相加等于m,则两数和能被k整除。故用桶记录取模数,把相加等于m的两个桶进行匹配。
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n,m;cin>>n>>m;
map<int,int>mp;
for (int i=1;i<=n;i++){
int x;cin>>x;
mp[x%m]++;//对取模数进行桶计数
}
int cnt = 0;
if (mp[0]) cnt++;
for (int i=1;i<=m/2;i++){
if (mp[i]&&!mp[m-i]) cnt += mp[i];
else if (!mp[i]&&mp[m-i]) cnt += mp[m-i];
//若匹配不了 直接加个数
else if (mp[i]&&mp[m-i]){
//若可以匹配 进行分类讨论
if (abs(mp[i]-mp[m-i])>=2) cnt += abs(mp[i]-mp[m-i]);
//若绝对值差大于等于2 那么他们子序列的个数即绝对值差
else cnt++;
//若小于2 那么只有一个子序列
}
}
cout<<cnt<<endl;
}
}
Global round 14 B. Phoenix and Puzzle
题意:给n个等腰直角三角形,问能否组成一个正方形
思路:数学。每个大正方形的最小正方形只能由两块三角形或四块三角形拼成,故n必被2或4整除;将n/=2或n/=4,得出n能组成几个小正方形,而这些小正方形只有个数等于平方数时才能变成大正方形。
注:判断平方数时,不能写sqrt(m)*sqrt(m)==m cf编译器会误判。但我自己的dev编译器却是正确的
#include <bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while (t--){
int n;cin>>n;
if (n==2||n==4)cout<<"YES"<<endl;
else if (n%2==0){
int m = n / 2;
double c = sqrt(m);
if (c==(int)c) cout<<"YES"<<endl;
else if (n%4==0){
m = n / 4;
double c = sqrt(m);
if (c==(int)c) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
else cout<<"NO"<<endl;
}
else if (n%4==0){
int m = n / 4;
double c = sqrt(m);
if (c==(int)c) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
else cout<<"NO"<<endl;
}
}
Round 116(div2) B. Update Files
题意:有n个结点,每两个结点每次连一条边,每次最多连k条边,问变成连通图需要几次。
思路:思维+数学。发现能连的边以2的指数倍增长,当k比2的指数倍小时,把剩下没连的结点数除以k即可
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
signed main(){
IOS
int t;cin>>t;
while (t--){
int n,k;cin>>n>>k;
int p = 1;
int sum = 0;
n -= 1;//结点1不需要连
while (p<k){
//枚举2的指数倍
n -= p;
p <<= 1;
sum ++;
}
if (n<=0) cout<<sum<<endl;
else {
//剩下的结点数直接除以k
sum += (n+k-1) / k;//向上取整
cout<<sum<<endl;
}
}
}