文章目录
- 1.[B. Same Parity Summands](https://codeforces.com/contest/1352/problem/B)
- 2.[C. Challenging Cliffs](https://codeforces.com/problemset/problem/1537/C)
- 3.[B. Sorted Adjacent Differences](https://codeforces.com/contest/1339/problem/B)
- 4.[C1. k-LCM (easy version)](https://codeforces.com/problemset/problem/1497/C1)
- 5.[B1. Palindrome Game (easy version)](https://codeforces.com/contest/1527/problem/B1)
- 6.[B. Before an Exam](https://codeforces.com/contest/4/problem/B)
- 7.[D. Corrupted Array](https://codeforces.com/problemset/problem/1512/D)
- 8.[D. Districts Connection](https://codeforces.com/contest/1433/problem/D)
- 9.[B. Jumps](https://codeforces.com/problemset/problem/1455/B)
- 10.[C. Mocha and Hiking](https://codeforces.com/problemset/problem/1559/C)
- 11.[C. A-B Palindrome](https://codeforces.com/problemset/problem/1512/C)
- 12.[B. Reverse Binary Strings](https://codeforces.com/contest/1437/problem/B)
- 13.[B. Sherlock and his girlfriend](https://codeforces.com/problemset/problem/776/B)
- 14.[A. Common Prefixes](https://codeforces.com/problemset/problem/1384/A)
- 15.[B. Paranoid String](https://codeforces.com/problemset/problem/1694/B)
- 16.[C. Make Equal With Mod](https://codeforces.com/problemset/problem/1656/C)
- 17.[B. M-arrays](https://codeforces.com/problemset/problem/1497/B)
- 18.[D. Rudolph and Christmas Tree](https://codeforces.com/problemset/problem/1846/D)
- 19.[B. Prinzessin der Verurteilung](https://codeforces.com/problemset/problem/1536/B)
- 20.[B. Make Almost Equal With Mod](https://codeforces.com/contest/1909/problem/B)
- 21.[C. Rudolf and the Another Competition](https://codeforces.com/problemset/problem/1846/C)
- 22.[B. Neighbor Grid](https://codeforces.com/problemset/problem/1375/B)
- 23.[B. Flip the Bits](https://codeforces.com/contest/1504/problem/B)
- 24.[C. Dora and Search](https://codeforces.com/contest/1793/problem/C)
- 25.[C. Build Permutation](https://codeforces.com/contest/1713/problem/C)
- 26.[B. Odd Sum Segments](https://codeforces.com/contest/1196/problem/B)
- 27.[B. Stairs](https://codeforces.com/problemset/problem/1419/B)
- 28.[B. Interesting Subarray](https://codeforces.com/problemset/problem/1270/B)
- 29[C. Differential Sorting](https://codeforces.com/problemset/problem/1635/C)
- 30.[B. Hemose Shopping](https://codeforces.com/problemset/problem/1592/B)
- 31.[D. A-B-C Sort](https://codeforces.com/problemset/problem/1674/D)
- 32.[D. Super-Permutation](https://codeforces.com/problemset/problem/1822/D)
- 33.[B. Madoka and the Elegant Gift](https://codeforces.com/problemset/problem/1647/B)
- 34.[B. Chat Order](https://codeforces.com/problemset/problem/637/B)
- 35.[A. Prefix Sum Primes](https://codeforces.com/problemset/problem/1149/A)
- 36.[B. Prefix Sum Addicts](https://codeforces.com/problemset/problem/1738/B)
- 37.[B. Chloe and the sequence](https://codeforces.com/problemset/problem/743/B)
- 38.[A. Almost Equal](https://codeforces.com/problemset/problem/1205/A)
- 39.[B. Restricted RPS](https://codeforces.com/problemset/problem/1245/B)
- 40.[B. Snow Walking Robot](https://codeforces.com/problemset/problem/1272/B)
- 41.[B. Morning Jogging](https://codeforces.com/problemset/problem/1517/B)
- 42.[B. Square Filling](https://codeforces.com/problemset/problem/1207/B)
- 43.[B. Easter Eggs](https://codeforces.com/problemset/problem/78/B)
- 44.[B. Omkar and Heavenly Tree](https://codeforces.com/contest/1583/problem/B)
- 45.[C. Dishonest Seller](https://codeforces.com/problemset/problem/779/C)
- 46.[B. Build the Permutation](https://codeforces.com/problemset/problem/1608/B)
- 47.[B. Diameter of Graph](https://codeforces.com/problemset/problem/1581/B)
- 48.[A. Vladik and flights](https://codeforces.com/problemset/problem/743/A)
- 49.[Problem - 264A - Codeforces](https://codeforces.com/problemset/problem/264/A)
- 50.[B. Equalize by Divide](https://codeforces.com/contest/1799/problem/B)
- 51.[B. New Year and the Treasure Geolocation](https://codeforces.com/problemset/problem/1091/B)
- 52.[B. Kuroni and Simple Strings](https://codeforces.com/problemset/problem/1305/B)
1.B. Same Parity Summands
一共有k个正整数模2相等,n为k个正整数的和
也就是说k个正整数要么都是偶数要么都是奇数
已知n和k构造这样一个序列,无解输出No
分情况讨论:
一.n是偶数:
1.k是偶数
两种情况皆可
(1)全是偶数:k-1个2,最后一个位置放剩下的–检验
(2)全是奇数:k-1个1,最后一个位置放剩下的
2.k是奇数–全是偶数
二.n是奇数
1.k是奇数–全是奇数
2.k是偶数–无解
以上弄麻烦了,直接对全是偶数和全是奇数分类即可,然后检验,因为只有全是偶数和全是奇数两种情况,不用再细分了
trick:
1.简单化,使得分类情况最少,分类往少的情况分
2.如果细节比较多,那么很容易出错,一种简单有效的方法是将序列构造出来,检验即可
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,k;
void solve() {cin>>n>>k;vector<int>ans1,ans2;//全是奇数for(int i=0;i<k-1;i++) ans1.push_back(1);ans1.push_back(n-k+1);if(n-k+1>0&&(n-k+1)%2==1){cout<<"Yes"<<endl;for(auto v:ans1) cout<<v<<' ';cout<<endl;return;}//全是偶数for(int i=0;i<k-1;i++) ans2.push_back(2);ans2.push_back(n-2*(k-1));if(n-2*(k-1)>0&&(n-2*(k-1))%2==0){cout<<"Yes"<<endl;for(auto v:ans2) cout<<v<<' ';cout<<endl;return;}cout<<"No"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
2.C. Challenging Cliffs
n座山,高度为hi
重新排列它们,使得第一座山的高度和最后一座山的高度的差越小越好
在高度差最小的基础上,需要难度越大越好
难度值:前一座山高度小于等于后一座山的数对的数量
如果一直升序的话,那么虽然难度值大但是h1和hn的差值就大了
优先考虑差值,有这样一种构造方法:升一次降一次或者降一次升一次
因为可以随便放置顺序,所以可以先升个序,然后比较两两相邻,看差值最小是多少将两者放在两端,然后中间交替放置
大概想出一个模糊的思路,具体实现细节仍需要再想想
可以这样,就是先确定好两端的数,然后中间的数先按顺序排好,然后双指针两头交替放置,至于哪头先放,可以都试一遍,然后验证一遍即可
还有一个问题就是如果差值最小的不止一对该怎么选择?放值小的还是值大的?都没关系应该,因为除去两个数的相对顺序大小是一定的,然后交替放置,这边不太确定,感觉无法证明,只能猜一下,然后事实证明错了,代码交上去错了
这边想错了,既然想要难度值最大,就得往最极端的方向考虑,让它尽可能一直升序,没办法了才降一次,而不是升降交替
先升序,找到一组差值最小的mini和mini+1,然后mini和mini+1分别放在最左端和最右端,然后中间先放mini+2到n(此时一直保持升序),然后再放1到mini-1,这样的话就只降了一次
trick:
1.当可以随便排顺序的时候,说明和顺序无关,那么先排个序
2.当题目要最的时候就往极端的方向想,贪心是一种思想,就是先理想化,最好是怎么样,实在没办法需要舍弃一些以达到最
3.如果对自己的思路不能证明的话,不能很肯定的话,那么其实还是一种猜,这样风险比较大,因为当代码写完才发现不对已经浪费了很多时间了,所以最好还是想能证明的方法,可以基本肯定的方法
#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sort(a+1,a+1+n);int minn=2e9,mini=-1;for(int i=1;i<=n-1;i++){if(abs(a[i]-a[i+1])<minn){minn=abs(a[i]-a[i+1]);mini=i;}}vector<int>ans;ans.push_back(a[mini]);for(int i=mini+2;i<=n;i++) ans.push_back(a[i]);for(int i=1;i<mini;i++) ans.push_back(a[i]);ans.push_back(a[mini+1]);for(auto v:ans) cout<<v<<' ';cout<<endl;
}
int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
3.B. Sorted Adjacent Differences
重新排列序列满足两两相邻差值非降序
先升序 -2 4 5 5 6 7
题目没说不存在,那么肯定可以构造出来,那么就有一种通用的万能的构造方法
升序完之后
首先找到最中间的一个,放进序列里,由于排完序之后数的位置差的越远,那么差值越大,所以就利用双指针,左边挑一个,右边挑一个
trick:
1.当可以随便排顺序的时候,说明和顺序无关,那么先排个序(这是前面做到过的trick,在这里派上用场了,正是因为这个才能继续想到后面的思路)
2.题目没说不存在,那么肯定可以构造出来,那么就有一种通用的万能的构造方法(往往比较简单,样例体现不出),通过题目给的信息猜测做法
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sort(a+1,a+1+n);int mid=(n+1)/2;vector<int>ans;ans.push_back(a[mid]);for(int i=mid-1,j=mid+1;i>=1;i--,j++){ans.push_back(a[j]);ans.push_back(a[i]);}if(n%2==0) ans.push_back(a[n]);for(auto v:ans) cout<<v<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
4.C1. k-LCM (easy version)
整数n和k(k为3)
找到3个正整数使得和为n,最小公倍数小于等于n的一半------每个数都得小于等于n/2并且大的数必须是小的数的倍数
答案总是存在—有一种万能通用构造方法
如果是偶数的话,那么最大的数即为n/2或者n/2-1
{
如果n/2为偶数,那么选择n/2,然后剩下的两个数分别为n/4和n/4
如果n/2-1为偶数,那么选择n/2-1,然后剩下两个数分别为n/2-1和2
}
如果是奇数的话,那么就是n/2,n/2和1
trcik:
1.有性质,则通过手玩举例子挖掘背后的性质----最小公倍数小于等于n的一半------每个数都得小于等于n/2并且大的数必须是小的数的倍数
2.答案总是存在—有一种万能通用构造方法
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,k;
void solve() {cin>>n>>k;if(n%2==0){if(n/2%2==0) cout<<n/2<<' '<<n/4<<' '<<n/4<<endl;else if((n/2-1)%2==0) cout<<n/2-1<<' '<<n/2-1<<' '<<2<<endl;}else cout<<n/2<<' '<<n/2<<' '<<1<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
5.B1. Palindrome Game (easy version)
长度为n的01串,回文(包含至少一个0)
Alice和Bob进行博弈,Alice先行
操作:把其中一个0变成1,花费1美元或者翻转整个字符串,不花费代价(前提是操作对象不是回文串且上一次对手操作不是翻转)
当字符全是1时,游戏结束,谁花费代价小谁赢,可以平局
一开始是回文偶数串,那么Alice先行,将其中一个0变成1,然后Bob就对称去变使得仍是回文串,直到最后Bob进行一个翻转,那么Bob赢了,赢两子
一开始是回文奇数串,且中间是0的话,那么Alice抢占先机,将中间的先变成1,那么就演变成第一种情况了,那么Alice赢
再特判n为1时,肯定是Bob赢
如果只有一个0的话,Bob赢
没有平局,因为至少有一个0,且一开始回文
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;if(n==1){cout<<"BOB"<<endl;return;}int cnt=0;for(int i=0;i<n;i++){if(s[i]=='0') cnt++;}if(cnt==1){cout<<"BOB"<<endl;return;}if(n%2==0) cout<<"BOB"<<endl;else{if(s[(n+1)/2-1]=='0') cout<<"ALICE"<<endl;else cout<<"BOB"<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
6.B. Before an Exam
Peter学习了d天,总共花了sum个小时
minTimei是第i天最小花费时间,maxTimei是第i天最大花费时间
算出学习d天的最小花费时间和最大花费时间,这样一个区间,如果sum在该区间,那么Yes,否则No
trick:
每天花费时间是有下限的,即每天首先得花minTime时间,这是一定的(想想有哪些是一定的,往一定的方向想,特别是做操作(动作)首先做什么是一定的,是必须的)
区间,有两端的限制,将两端的限制转化为一端的限制,可以把所有的minTime减去,那么就没有下限的限制了,就只有上限了,那么就可以贪心了
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=50;
int mint[N],maxt[N];
int d,sum;
int last[N];
void solve() {cin>>d>>sum;int l=0,r=0;for(int i=1;i<=d;i++) {cin>>mint[i]>>maxt[i];l+=mint[i];r+=maxt[i];}if(!(sum>=l&&sum<=r)){cout<<"NO"<<endl;return;}for(int i=1;i<=d;i++) sum-=mint[i];for(int i=1;i<=d;i++){if(sum>=maxt[i]-mint[i]){sum-=(maxt[i]-mint[i]);mint[i]=maxt[i];}else{mint[i]+=sum;sum=0;}}cout<<"YES"<<endl;for(int i=1;i<=d;i++) cout<<mint[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
7.D. Corrupted Array
长度为n+2的数组b,数组b中有n个数是a的,有一个数是a数组的和,还一个数任意
求数组a,无解输出-1
与顺序无关,先排个序
由于b中有一数是n个数的和,所以该数要么是最大的,要么是次大的,如果是次大的,那么只要检验前n个数的和是不是和次大的数相等,如果是最大的,假设其它n+1个数的和为sum,那么一个一个枚举,检验sum-a[i]是否等于最大的数
trick:
前面已经总结过的,与顺序无关,先排个序,这也是这题解题的必要步骤
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int b[N];
int n;
void solve() {cin>>n;int m=n+2;int sum=0;for(int i=1;i<=m;i++) cin>>b[i],sum+=b[i];sort(b+1,b+1+m);//假设n个数的和为次大的if(sum-b[n+1]-b[n+2]==b[n+1]){for(int i=1;i<=n;i++) cout<<b[i]<<' ';cout<<endl;return;}//假设n个数的和为最大int idx=-1;for(int i=1;i<=n+1;i++){if(sum-b[i]-b[n+2]==b[n+2]){idx=i;break;}}if(idx!=-1){for(int i=1;i<=n+1;i++){if(idx==i) continue;cout<<b[i]<<' ';}cout<<endl;return;}cout<<-1<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
8.D. Districts Connection
长度为n的数组a
第i个地区属于帮派ai
用n-1条边使得所有地区连通,即生成一棵树,但是有限制,即属于同一帮派的两个地区不能直接相连(可以间接)
先将属于同一帮派的地区归类在一起,利用一个桶排序,记录每个帮派有哪些地区,然后按帮派顺序,每次输出属于其的一个地区
但是这样爆空间了,帮派标号可以达到1e9,但是帮派总数也就5000,所以进行哈希,哈希函数为f(x)=x%5000,这样会冲突,没办法好像
这波属实脑筋急转弯,脑子没转过来,树只要连通且不成环即可, 可以很多点和一个点相连
将和第一个点不是同一帮派的都和第一个点相连,将和第一个是同一帮派的和第一个与第一个点不是同一帮派的点相连
trick:
这题算作一个脑筋急转弯的案例,脑子不容易转过来
1.对树的性质没记牢,没记清楚 ,可以很多点和一个点相连
2.样例中也有很多是一个点和若干点想连,没有仔细分析全部样例,样例是给了暗示的,图论的话还是把样例的图给画出来,更加直观
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=5010;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];vector<int>ans1,ans2;for(int i=2;i<=n;i++){if(a[i]==a[1]) ans1.push_back(i);//存放和第一个帮派相同的点}for(int i=2;i<=n;i++){if(a[i]!=a[1]) ans2.push_back(i);}int idx=-1;for(int i=1;i<=n;i++){if(a[i]!=a[1]){idx=i;//idx为第一个和第一个点帮派不同的点break;}}if(idx==-1){cout<<"NO"<<endl;return;}cout<<"YES"<<endl;for(auto v:ans2) cout<<1<<' '<<v<<endl;for(auto v:ans1) cout<<idx<<' '<<v<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
9.B. Jumps
初始在x轴的原点,目标是移动到x
操作:往右跳k步或者往左跳一步(假设这是第k次跳)
问最少几次操作(一定可以到达x)
凑 6
1 2 3 4 5 6
(1+n)*n/2
凑7
1 2 3 4 5 6
将某些数变成-1
凑8
1 2 3 4 5 6 7
本质上是一个凑数的问题,从1到n,将其中某些数变成-1,也就是说当得到了1+2+3…+n后,可以减2,减3,…减n,那么1到n-2的任何一个数都是可得的,操作次数为n,但是由于不能减1,所以如果比1+2+…+n少1的话,那么就多一次减1的操作
所以要使得操作次数最少,那么就找到第一个大于等于x的1+2+…n
trick:
凑数问题,和数学数字联系紧密,总之还是得对数字敏感,更多的是一种规律性的东西,需要手玩找规律
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int x;
void solve() {cin>>x;vector<int>ans;int sum=0;for(int i=1;i<=2000;i++){sum+=i;ans.push_back(sum);}int pos=lower_bound(ans.begin(),ans.end(),x)-ans.begin();if(ans[pos]==x){cout<<pos+1<<endl;}else if(ans[pos]==x+1){cout<<pos+2<<endl;}else cout<<pos+1<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
10.C. Mocha and Hiking
n+1个村庄
图
计划一条路经过所有村庄有且仅有一次
首先,1到2,2到3,…一直到n-1一定是一定的
如果an为0的话,那么直接从1开始到n+1即可
如果a1为1的话,那么从n+1到1再一直到n
否则遍历,找到ai为0且ai+1为1的,如果能找到,就可以从1到i到n+1再到i+1再到n
否则输出-1
trick:
1.图论的题目就是手玩画图
2.图论的题目一般要特判n为1的情况
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e4+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(n==1){if(a[1]==0) cout<<1<<' '<<2<<endl;else cout<<2<<' '<<1<<endl;return;}if(a[1]==1){cout<<n+1<<' ';for(int i=1;i<=n;i++) cout<<i<<' ';cout<<endl;return;}if(a[n]==0){for(int i=1;i<=n+1;i++) cout<<i<<' ';cout<<endl;return;}else{for(int i=1;i<=n-1;i++){if(a[i]==0&&a[i+1]==1){for(int j=1;j<=i;j++) cout<<j<<' ';cout<<n+1<<' ';for(int j=i+1;j<=n;j++) cout<<j<<' ';cout<<endl;return;}}}cout<<-1<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
11.C. A-B Palindrome
两个整数a+b
给定一个长度为a+b的字符串s(0,1,?)
用0和1去替换问号,使字符串变成回文字符串,刚好要a个0,b个1
无解则输出-1
如果a是奇数,那么b必须是偶数,0必须放在最中间,反之同理
先将只有一边是问号的补成对称,然后将0的个数补全,其它问号填1,最后检验
trick:
情况比较多的话,将序列构造出来再检验(之前做题积累过的,在这里又用到了)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int a,b;
string s;
void solve() {cin>>a>>b;cin>>s;//先将只有一边是问号的补全for(int i=0,j=a+b-1;i<=j;i++,j--){if(i==j) continue;if(s[i]!='?'&&s[j]=='?') s[j]=s[i];else if(s[i]=='?'&&s[j]!='?') s[i]=s[j];}if((a+b)%2==1){if(a%2==1){if(s[(a+b)/2]=='?') s[(a+b)/2]='0';else if(s[(a+b)/2]=='1'){cout<<-1<<endl;return;}}else{if(s[(a+b)/2]=='?') s[(a+b)/2]='1';else if(s[(a+b)/2]=='0'){cout<<-1<<endl;return;}}}int cnt_0=0,cnt_1=0;for(int i=0,j=a+b-1;i<=j;i++,j--){if(i==j) continue;if(s[i]=='0') cnt_0+=2;else if(s[i]=='1') cnt_1+=2;}if((a+b)%2==1){if(a%2==1) cnt_0++;else cnt_1++;}for(int i=0,j=a+b-1;i<=j;i++,j--){if(s[i]!='?') continue;if(cnt_0<a){s[i]=s[j]='0';cnt_0+=2;}else break;}for(int i=0,j=a+b-1;i<=j;i++,j--){if(s[i]!='?') continue;s[i]=s[j]='1';}//检验for(int i=0,j=a+b-1;i<=j;i++,j--){if(s[i]!=s[j]){cout<<-1<<endl;return;}}int sum_0=0,sum_1=0;for(int i=0;i<a+b;i++){if(s[i]=='0') sum_0++;else sum_1++;}if(sum_0!=a||sum_1!=b) cout<<-1<<endl;else{for(int i=0;i<a+b;i++) cout<<s[i];cout<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
12.B. Reverse Binary Strings
长度为n的字符串(n为偶数),有n/2个0和n/2个1
操作:将连续子串reverse
问最少几次操作可以使得字符串交替
贪心,遍历模拟,但是这样很容易出错,很容易死循环以及细节不好把握
显然每次翻转只能改变子串的两端的字符,所以每次翻转最多改变一个连续两个的1和一个连续的两个的0
即为每次操作可以使得连续的0和1的部分的连续长度减一
所以最小的操作次数即为连续的两个0和1的数量的最大值
trick:
1.最通用的方法还是手玩,重点关注做一次操作(动作)后发生了什么变化,可以发现每次操作最多改变一个连续两个的1和一个连续的两个的0
2.手玩之造样例,造复杂,极端的样例,比如在这里就好多个1连续
2.关于string的reverse函数,可以部分翻转,比如翻转[i,j],则reverse(s.begin()+i,s.begin()+j+1)(基础知识,在这里没用到)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;int cnt_0=0,cnt_1=0;for(int i=1;i<(int)s.size();i++){if(s[i]==s[i-1]&&s[i]=='0') cnt_0++;else if(s[i]==s[i-1]&&s[i]=='1') cnt_1++;}cout<<max(cnt_0,cnt_1)<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
13.B. Sherlock and his girlfriend
一共有n件珠宝
价格分别为2,3,…n+1
如果其中一件是另一件的素因子,那么颜色不能相同
要求用最少的颜色
所有质数颜色肯定都可以相同,所有合数颜色肯定都可以相同,合数可以唯一分解成若干个质数的乘积,然后最多两种颜色就够了
坑点:
读错题目,the price of one piece is a prime divisor of the price of the other piece的意思是其中一个是另一个的质因数,指的是其中一个是质数同时使另一个数的因数,而不是说倍数是质数
trick:
任意合数都可以分解成若干个质数的乘积(唯一)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
bool st[N];
int n;
int cnt;
int prime[N];
map<int,int>mp;
//欧拉筛
void get_prime(int n){for(int i=2;i<=n;i++){if(!st[i]) prime[cnt++]=i;for(int j=0;prime[j]<=n/i;j++){st[prime[j]*i]=true;if(i%prime[j]==0) break;}}
}
void solve() {cin>>n;if(n<=2){cout<<1<<endl;for(int i=2;i<=n+1;i++) cout<<1<<' ';cout<<endl;return;}get_prime(n+1); cout<<endl;cout<<2<<endl;for(int i=2;i<=n+1;i++){if(!st[i]) cout<<1<<' ';else cout<<2<<' ';}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
14.A. Common Prefixes
长度为n的数组a
构造n+1个字符串,使得si和si+1的最长公共前缀长度为ai(答案总是存在)
坑点:如果多个样例,且用了数组,且数组并不是每个样例进行输入的,需要重新清空
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=110;
int a[N];
string s[N];
int n;
void solve() {cin>>n;for(int i=0;i<=n;i++) s[i].clear(); for(int i=1;i<=n;i++) cin>>a[i];for(int i=0;i<55;i++) s[0]+='a';for(int i=1;i<=n;i++){for(int j=0;j<a[i];j++) s[i]+=s[i-1][j];if(s[i-1][a[i]]=='z') s[i]+='a';else s[i]+=(char)(s[i-1][a[i]]+1);for(int j=a[i]+1;j<55;j++) s[i]+=s[i-1][j];}for(int i=0;i<=n;i++) cout<<s[i]<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
15.B. Paranoid String
长度为n的字符串
操作:将01子串替换为1或者将10子串替换为0(只有1和0在一起,就可以把左边的删掉)
如果子串[l,r]可以通过m-1次操作变成长度为1,那么称为paranoid
求有几个子串[l,r]是偏执字符串
当s[i]和s[i+1]相等时,以i+1结尾就会产生i+1个非paranoid字符串,总共的连续子串共n*(n+1)/2个,用总共的减去非的即可
trick:
1.求有几个,可以从贡献度的角度考虑,思考方向一般是一边遍历,一边对答案产生贡献
2.手玩, 构造复杂样例
3.手玩,重点关注操作(动作)后有什么变化
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;int res=0;for(int i=0;i<n-1;i++){if(s[i]==s[i+1]) res+=i+1;}cout<<n*(n+1)/2-res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
16.C. Make Equal With Mod
长度为n的数组a(非负)
操作:每个数对x(x大于等于2)取模
次数不限
问能否让所有元素相等
手玩,因为次数不限,有一个万能的方法,就是从大到小模它自己,变成0,只要大于1即可(只要不存在1就Yes)
如果存在1的话,由于1不能再变了,所以其它都必须变成1,也是从大到小,模比它小1的数,如果比它小1的数存在的话,那么No
如果1和0同时存在的话,No
trick:
1.顺序无关,先排个序
2.手玩
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve() {cin>>n;map<int,int>mp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=1;sort(a+1,a+1+n);if(mp[1]==0){//不存在1cout<<"Yes"<<endl;return;}if(mp[1]&&mp[0]){//1和0同时存在cout<<"No"<<endl;return;}//存在1for(int i=n;i>=1;i--){if(mp[a[i]-1]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
17.B. M-arrays
长度为n的数组a
将数组分成几个数组
如果数组中所有相邻两个数的和是m的倍数,那么m可分(只有一个元素的的数组m可分)
求最少可以分成几个m可分数组
与顺序无关,先排个序
如果每个数组都一个元素,那么数量多,尽可能每个数组元素多一些
桶计数
贪心
然后从头遍历,往后找,对于m1,m2,m3…小于等于最大的两个数的和,然后看mk-该数是否存在,如果存在,就放到该集合中
这样错了,这样的话,假设111 7,就是分成1 7,1,1,实际上可以分成1 7 1,1
和是m的倍数,实际上就是凑m,将所有数对m取模
然后桶计数,相加为m的两个数进行配对,如果两者相差的个数小于等于1,就只需要一个集合,多出去的单独成集合
trick:
和是m的倍数,实际上就是凑m,所有数对m取模,当然取模后为0的情况要特判
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
void solve() {cin>>n>>m;map<int,int>mp;set<int>s;for(int i=1;i<=n;i++) cin>>a[i],a[i]%=m,mp[a[i]]++,s.insert(a[i]);vector<int>ans;while(s.size()){ans.push_back(*s.begin());s.erase(s.begin());}
// for(int i=0;i<(int)ans.size();i++) cout<<ans[i]<<' '<<mp[ans[i]]<<endl;
// cout<<endl;int res=0;// 特判和为0的情况if(mp[0]) res++;for(int i=0;i<(int)ans.size();i++){if(mp[ans[i]]==0||ans[i]==0) continue;if(abs(mp[ans[i]]-mp[m-ans[i]])<=1) res++;else res=res+abs(mp[ans[i]]-mp[m-ans[i]]);mp[ans[i]]=mp[m-ans[i]]=0;}cout<<res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
18.D. Rudolph and Christmas Tree
一共有n个树枝,每个树枝(等腰三角形)的底均为d,高为h
然后给定n个树状底部所在的高度
求树枝总面积
按照底部高度从高到低排序,然后如果它的顶部在上一个的底部上面,那么就需要减去被覆盖的面积,被覆盖的高已知,利用相似求面积
比较直,没有弯弯绕绕的
#include<bits/stdc++.h>
#include<cstdio>
#define endl '\n'
#define int long long
using namespace std;
int n,d,h;
const int N=2e5+10;
int y[N];
void solve() {cin>>n>>d>>h;for(int i=1;i<=n;i++) cin>>y[i];sort(y+1,y+1+n);reverse(y+1,y+1+n);double area=(double)d*h/2;double ans=area;for(int i=2;i<=n;i++){ans+=area;if(y[i]+h>y[i-1]){double h1=y[i]+h-y[i-1];double d1=h1*d/h;ans-=h1*d1/2;}}printf("%.8f\n",ans);
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
19.B. Prinzessin der Verurteilung
长度为n的字符串(均为小写字母)
MEX:最短的字符串没有作为一个连续的子串出现(空字符串不允许)
求MEX
如果某个字符没有出现,那么该字符即为MEX
长度为1的字符串共26种
长度为2的字符串共26 * 26=676种
长度为3的字符串共26 * 26 * 26=17576种
而字符串长度最多1000,所以MEX的长度最长也就3
直接暴力枚举
trick:
1.数据比较小,直接暴力
2.string种有find函数,可以查找字符串,s.find(tmp),如果找不到,那么s.find==s.npos,时间复杂度为O(n * m)
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;for(int i=1;i<=26;i++){string tmp;tmp+=(char)('a'+i-1);if(s.find(tmp)==s.npos){cout<<tmp<<endl;return;}}for(int i=1;i<=26;i++){for(int j=1;j<=26;j++){string tmp;tmp+=(char)('a'+i-1);tmp+=(char)('a'+j-1);if(s.find(tmp)==s.npos){cout<<tmp<<endl;return;}}}for(int i=1;i<=26;i++){for(int j=1;j<=26;j++){for(int k=1;k<=26;k++){string tmp;tmp+=(char)('a'+i-1);tmp+=(char)('a'+j-1);tmp+=(char)('a'+k-1);if(s.find(tmp)==s.npos){cout<<tmp<<endl;return;}}}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
20.B. Make Almost Equal With Mod
长度为n的数组a,所有数不同
操作:对于所有数,对k取模(总是存在一个k在[1,1e8]使得最终只剩刚好两个数不同)
必须有且仅有一次
输出k
与顺序无关,先排个序
如果有奇数,有偶数,那么k为2
如果全是偶数,那么k可能为4
如果全是奇数,那么k可能为4
通过手玩,发现答案为2的幂次,一开始是想着如果有奇数和偶数的话,那么2即可,然后如果全是偶数的话,然后如果有相差2的偶数,那么4即可,后面如果有相差6的偶数,那么8即可,于是进行推广,2的幂次
证明:将十进制转化为二进制,对2取模即为最后一位,对4取模即为最后两位,…从最低位开始看,如果存在两个数某一位不一样(0和1不一样),假设是第x位,那么模 2 x − 1 2^{x-1} 2x−1即可,所以这样是必定可以有解的,因为题目说必然存在解,所以不可能所有数都相同
trick:
将十进制转化为二进制,对2取模即为最后一位,对4取模即为最后两位…
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define INF 1e18
using namespace std;
const int N=110;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];set<int>s;for(int i=1;i<=n;i++){s.insert(a[i]%2);}if(s.size()==2){cout<<2<<endl;return;}for(int i=4;i<=INF;i*=2){s.clear();for(int j=1;j<=n;j++){s.insert(a[j]%i);}if(s.size()==2){cout<<i<<endl;return;}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
21.C. Rudolf and the Another Competition
共有n名参赛者,R编号为1
比赛共m个问题,共h分钟
tij表示第i个参赛者解决第j个问题需要的时间
均按照最佳顺序解题,问R的名次
最佳顺序:从时间最短的开始贪心,贪的越多越好
坑点:
罚时并不是按照花费的时间来的,而是做完该题已经过了多长时间,然后全部加起来
方法是用sum记录花费的时间,然后罚时time每次加上sum
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int n,m,h;
struct node{int num,time,idx;bool operator<(const node &W)const{if(time==W.time&&num==W.num) return idx<W.idx;if(num==W.num) return time<W.time;return num>W.num;}
}q[N];
void solve() {cin>>n>>m>>h;for(int i=0;i<n;i++){multiset<int>s;for(int j=0;j<m;j++){int x;cin>>x;s.insert(x);}int num=0,time=0,sum=0;while(s.size()){if(sum+*s.begin()<=h){num++;sum+=*s.begin();time+=sum;s.erase(s.begin());}else break;}q[i]={num,time,i+1};}
// for(int i=0;i<n;i++) cout<<q[i].num<<' '<<q[i].time<<endl;sort(q,q+n);for(int i=0;i<n;i++){if(q[i].idx==1){cout<<i+1<<endl;return;}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
22.B. Neighbor Grid
n行m列,均为非负整数
操作:将任意数字增加1,使得相邻单元格大于0的个数刚好等于该数(0不用管)
次数不限
问能否,如果能,则输出如何改
坑点:YES,NO
数据比较小,直接暴力
操作中加1或不变是一定的,只能变大或不变,不能变小,也就是说只能加不能减
那么就遍历,如果大于0的话,那么就统计周围有几个数是大于0的话,如果刚好的话就不用管,如果数x小于num的话,就让x等于num,如果数x大于num的话,就需要在周围补足x个,那么如何放置呢?如果往上和往左放置的话,会影响之前的,方法不可行
其实还有一个重要的点,只增不减,操作次数不限,那么我们可以尽可能大的利用可以一直加这个点,我们可以将所有数都填满,然后统计每个格子周围格子数即可
trick:
找到操作不变的点,一个是只增不减(动作),另外就是操作次数不限(次数),那么就尽可能的一直加,将所有数都填满
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=310;
int a[N][N];
int n,m;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
void solve() {cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(a[i][j]==0) a[i][j]++;}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){int cnt=0;//周围有几个数是大于0的for(int k=0;k<4;k++){int tx=i+dx[k],ty=j+dy[k];if(tx<1||tx>n||ty<1||ty>m) continue;if(a[tx][ty]>0) cnt++;}if(a[i][j]>cnt){cout<<"NO"<<endl;return;}a[i][j]=cnt;}}cout<<"YES"<<endl;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cout<<a[i][j]<<' ';}cout<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
23.B. Flip the Bits
长度为n的01字符串a和b
操作:取a的前缀(保证0和1的个数相等),将0变成1,将1变成0
次数不限
问能否将a变成b
想要翻转[l,r]的话,只要翻转[1,r],再翻转[1,l],这是一定可以操作的,只要操作合法,即0和1个数相同,但其实这样不佳,因为这样是翻转连续的,而有可能是断断续续的,所以最佳还是一个一个翻转,由于后面会影响前面, 而前面不会影响后面,所以就从后往前一个一个翻转,记录翻转的次数,因为奇数次翻转就是互换,偶数次翻转就是不变
trick:
1.对于翻转,看翻转奇数次和偶数次,翻转奇数次就是翻转,翻转偶数次就是不变,这是相对于单个个体来说的
2.如果后面操作会影响前面做过的操作,那么就反着来,调换操作顺序,使得后面操作不会影响之前做的,这个是思考的一个方向,非必要
3.如果要将给定串变成目标串,一个很好的方向是一个字符一个字符变动,因为给定的串断断续续,很难能够连续变动成功
4.统计0和1的个数是否相等,一个方法是遇到1就计数加1,遇到0就计数减1,如果等于0则个数相等,否则不等
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=3e5+10;
int n;
int sum[N];
string a,b;
void solve() {cin>>n;cin>>a>>b;int sum1=0;for(int i=0;i<n;i++){if(a[i]=='1') sum1++;else sum1--;sum[i]=sum1;}int cnt=0;//翻转次数for(int i=n-1;i>=0;i--){if(a[i]!=b[i]){//需要奇数次翻转if(cnt%2==0){//已经翻转了偶数次if(sum[i]==0) cnt++;//操作合法else{cout<<"No"<<endl;return;}}}else{//需要偶数次翻转if(cnt%2==1){//已经翻转了奇数次if(sum[i]==0) cnt++;//操作合法else{cout<<"No"<<endl;return;}}}}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
24.C. Dora and Search
长度为n的数组a(全排列)
找到l和r,满足[l,r]种的最小值和最大值不在开头和结尾,如果无解,输出-1
直接模拟,双指针
利用set,快速得到最大值和最小值
坑点:
1.遍历set,for(auto it=s.begin();it!=s.end();it++),s.end()是最后一个元素下一个位置的迭代器,要想得到最后一个元素,应该*(–s.end())
2.模拟很容易死循环,当用到栈,队列,set等取头和尾的时候,一定要加一个条件,判断是否为空
3.如果要一直判断的话,最外层套一个while,然后if(条件x)判断能否进去,再while(条件x)进行循环
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n;
void solve() {cin >> n;set<int>s;for (int i = 1; i <= n; i++) cin >> a[i], s.insert(a[i]);int i = 1, j = n;while (1) {if (s.size()>1 && (a[i] == *s.begin() || a[i] == *(--s.end()) || a[j] == *s.begin() || a[j] == *(--s.end()))) {while (s.size()>1 && (a[i] == *s.begin() || a[i] == *(--s.end()) || a[j] == *s.begin() || a[j] == *(--s.end()))) {if (s.size()>1 && (a[i] == *s.begin() || a[i] == *(--s.end()))) {if (a[i] == *s.begin()) {s.erase(s.begin());i++;} if (a[i] == *(--s.end())) {s.erase(--s.end());i++;}}if (s.size()>1 && (a[j] == *s.begin() || a[j] == *(--s.end()))) {if (a[j] == *s.begin()) {s.erase(s.begin());j--;} if (a[j] == *(--s.end())) {s.erase(--s.end());j--;}}}}else break;}if(i>=j) cout << -1 << endl;else cout<<i<<' '<<j<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t = 1;cin >> t;while (t--) {solve();}return 0;
}
25.C. Build Permutation
good:所有i+ai都是完全平方数
给定n
构造一个排列(0到n-1各用一次),使得排列good,无解输出-1
n最大为1e5,那么数范围为[0,1e5-1],再加上索引,范围为[0,2e5-2],最多447个完全平方数
447的平方=199809
448的平方=200704
然后遍历447个完全平方数,分别减去0到n-1,得到的索引如果在0到n-1并且还没用过就可以,但是这样构造,构造不出的时候可能是有其它解的
加上索引成为完全平方数,只增不减,一般凑完全平方数都是凑成大于等于它的第一个完全平方数
手玩
比如0 1 2 3 4 5 6 7 8 9
9凑成16,需要7,那么我们就可以将7到9都凑成16
然后从6开始,6凑成9,需要3,我们就可以将3到6都凑成9
然后从2开始,2凑成4,需要2,将2凑成4
然后从1开始,凑成1,需要0,就可以将0到1凑成1
按照这样的方式,总是可以成功构造,没有无解的情况
trick:
凑(加上一个数成为)平方数一般都是凑成大于等于该数的第一个平方数
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n;
void solve() {cin>>n;int r=n-1;int num;//找到大于等于r的第一个平方数if((int)sqrt(r)*(int)sqrt(r)==r) num=r;else num=((int)sqrt(r)+1)*((int)sqrt(r)+1);int l=num-r;while(l>=0){for(int i=r;i>=l;i--) a[i]=num-i;if(l==0) break;r=l-1;if((int)sqrt(r)*(int)sqrt(r)==r) num=r;else num=((int)sqrt(r)+1)*((int)sqrt(r)+1);l=num-r;}for(int i=0;i<n;i++) cout<<a[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
26.B. Odd Sum Segments
长度为n的数组a
将其分成刚好k个非空子段,每个子段的和均为奇数,相对顺序不能变
问能否划分
坑点:YES,NO
贪心,只要有奇数就划分出来
这题很简单的,竟然折腾了好久
坑点:
当和为奇数的时候,就划分一段,讨论最后一段是偶数的情况时,忘记加上最后一段了
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n, k;
void solve() {cin >> n >> k;int res = 0;for (int i = 1; i <= n; i++) cin >> a[i], res += a[i];int cnt = 0, sum = 0;vector<int>ans;for (int i = 1; i <= n; i++) {sum += a[i];if (sum % 2 == 1) {ans.push_back(i);cnt++;sum = 0;}}if (sum == 0) { //最后一段是奇数,说明可以分成cnt段都是奇数if (cnt < k || (cnt - k) % 2 == 1) cout << "NO" << endl; //少于k段或者多出来奇数个else {cout << "YES" << endl;for (int i = 0; i < k - 1; i++) cout << ans[i] << ' ';cout << n << endl;}} else { //最后一段是偶数cnt++;if (cnt <= k || (cnt - k) % 2 == 0) cout << "NO" << endl; //少于等于k段或者多出来偶数个else {cout << "YES" << endl;for (int i = 0; i < k - 1; i++) cout << ans[i] << ' ';cout << n << endl;}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t = 1;cin >> t;while (t--) {solve();}return 0;
}
27.B. Stairs
题目给出 t 组数据,对于每一组数据: 把呈阶梯状的图形称为“阶梯”,把最长边为X 的阶梯称为“X级阶梯”。如图是一个7级阶梯。如果一个“X级阶梯”可被 X 个边长为正整数的正方形恰好覆盖,那么称之为“好阶梯”。 现有n 个边长为1 的正方形,问最多可以搭出多少个不同的“好阶梯”?
手玩,很明显是找规律的问题
发现从小到大是2的n次方-1级阶梯,换算成小正方形:等差数列求和,x级阶梯就是1+2+4…+x=(1+x)* x /2
只要从最小的开始贪就行了
坑点:
用位运算求2的幂次的时候,别忘了强制转化成long long,1ll<<x
trick:
辨别出是规律题之后,就直接手玩找规律
#include<bits/stdc++.h>
#define endl '\n'
#define INF 1e18
#define int long long
using namespace std;
int x;
vector<int>res;
void solve() {cin>>x;int ans=0;for(int i=0;i<(int)res.size();i++){int a=(res[i]+1)*res[i]/2;if(x>=a){ans++;x-=a;}else break;}cout<<ans<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;for(int i=1;i<=63;i++){res.push_back((1ll<<i)-1);}cin>>t;while(t--) {solve();}return 0;
}
28.B. Interesting Subarray
长度为n的数组a(数的范围在[0,1e9]
interesting:最大元素减去最小元素大于等于数组的个数
问给定数组a是否存在一个连续子序列是interesting,如果不存在的话就输出NO
坑点:YES,NO
如果全都是差1的话,那么k个数最大减最小就是k-1,就小于k,就输出
只要有一对数是差2的,那么就输出YES
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=2;i<=n;i++){if(abs(a[i]-a[i-1])>=2){cout<<"YES"<<endl;cout<<i-1<<' '<<i <<endl;return;}}cout<<"NO"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
29C. Differential Sorting
长度为n的数组a(n大于等于3)
数的范围[-1e9,1e9](也要重点关注数的范围,包括数的大小和正负)
操作:对于ax,在后面找两个数,用前面的数减后面的数去替换ax
最多n次
使得数组非降序,无解输出-1
可以发现,最后两个数是定死的,不能变的,所以前面所有数都要小于最后两个数
我们可以每次都用倒数第二个数减倒数第一个数,这样的话,就都是一样的了,同样满足非降序,最后检验即可
但是如果最后两个数都是负数的话,那么相减的话就会变大,必须全部都非降序才行,然后操作次数为0
坑点:
1.小数减大数不一定变小,减去一个负数相当于加上一个正数,是会变大的
2.也要重点关注数的范围,包括数的大小和正负
trick:
1.最多n次,一般就是差不多要把n次用完(不一定用完)
2.当需要输出两者相联系的话,一般可以把其中一个定死,也可以把两个都定死
3.把给定数组变成要求的数组,其中一个比较好的方向是一个一个变
4.要求非降序,并不是升序,其中一个思路是全部变成一样的
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 2e5 + 10;
int a[N];
int n;
void solve() {cin >> n;for (int i = 1; i <= n; i++) cin >> a[i];if (a[n - 1] > a[n]) {cout << -1 << endl;return;}if (a[n - 1] < 0 && a[n] < 0) {for (int i = 2; i <= n; i++) {if (a[i] < a[i - 1]) {cout << -1 << endl;return;}}cout<<0<<endl;return;}cout << n - 2 << endl;for (int i = 1; i <= n - 2; i++) cout << i << ' ' << n - 1 << ' ' << n << endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t = 1;cin >> t;while (t--) {solve();}return 0;
}
30.B. Hemose Shopping
长度为n的数组a(数[1,1e9])
操作:两个索引差大于等于x,就可以交换
次数不限
排成非降序
这是之前总结过的类型,满足某个条件就可以两两交换,看能否通过中间变量来变成自由变量,可以将 a 1 a_1 a1和 a n a_n an作为中间变量,然后[1,x+1]以及[n-x,,n]均可以互换,[x+2,n-x-1]是不能动的
trick:
都是之前总结过的,这题就当案例
源代码如下,一直RE,也不知道哪里越界了
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,x;
void solve() {cin>>n>>x;for(int i=1;i<=n;i++) cin>>a[i];int r=1+x,l=n-x;//[r,n]均可以和1交换,[1,l]均可以和n交换,故[1,l]以及[r,n]均可以互换,[l+1,r-1]不能动if(l>=r){cout<<"Yes"<<endl;return;}if(l==0&&r==n+1){for(int i=2;i<=n;i++){if(a[i]<a[i-1]){cout<<"No"<<endl;return;}}}for(int i=l+2;i<=r-1;i++){if(a[i]<a[i-1]){cout<<"No"<<endl;return;}}vector<int>ans;for(int i=1;i<=l;i++) ans.push_back(a[i]);for(int i=r;i<=n;i++) ans.push_back(a[i]);sort(ans.begin(),ans.end());if(ans[l-1]>a[l+1]||ans[l]<a[r-1]){cout<<"No"<<endl;return;}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
实际上最好的方法还是把序列构造出来进行验证
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,x;
void solve() {cin>>n>>x;for(int i=1;i<=n;i++) cin>>a[i];int r=1+x,l=n-x;//[r,n]均可以和1交换,[1,l]均可以和n交换,故[1,l]以及[r,n]均可以互换,[l+1,r-1]不能动vector<int>ans1,ans;for(int i=1;i<=l;i++) ans1.push_back(a[i]);for(int i=r;i<=n;i++) ans1.push_back(a[i]);sort(ans1.begin(),ans1.end());for(int i=0;i<l;i++) ans.push_back(ans1[i]);for(int i=l+1;i<=r-1;i++) ans.push_back(a[i]);for(int i=l;i<(int)ans1.size();i++) ans.push_back(ans1[i]);for(int i=1;i<(int)ans.size();i++){if(ans[i]<ans[i-1]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
31.D. A-B-C Sort
长度为n的数组a(数[1,1e6])
数组b和c为空
操作:
while(a不为空),将a的最后一个元素放在b的中间
while(b不为空),将b的中间元素放在c的末尾
问可以使c非降序吗
通过手玩,发现从后往前,两个数两个数,必须依次递减
trick:
没有思路就手玩
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(n==1||n==2){cout<<"Yes"<<endl;return;}if(n%2==1){int minn=min(a[n],a[n-1]);for(int i=n-2;i>=2;i-=2){if(a[i]>minn||a[i-1]>minn){cout<<"No"<<endl;return;}minn=min(a[i],a[i-1]);}if(a[1]>minn){cout<<"No"<<endl;return;}cout<<"Yes"<<endl;}else{int minn=min(a[n],a[n-1]);for(int i=n-2;i>=2;i-=2){if(a[i]>minn||a[i-1]>minn){cout<<"No"<<endl;return;}minn=min(a[i],a[i-1]);}cout<<"Yes"<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
32.D. Super-Permutation
先确定数组b,通过样例,发现第一个填0,然后隔一个加个1,第二个填n-1,隔一个减个1
然后通过数组b反过来确定数组a
再检验
trick:
构造序列题,手玩,找规律
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N],ans[N];
int n;
void solve() {cin>>n;int cnt=0;for(int i=1;i<=n;i+=2){a[i]=cnt;cnt++;}cnt=n-1;for(int i=2;i<=n;i+=2){a[i]=cnt;cnt--;}ans[1]=n;for(int i=2;i<=n;i++){if(a[i-1]<a[i]) ans[i]=a[i]-a[i-1];else ans[i]=a[i]+n-a[i-1];}set<int>s;for(int i=1;i<=n;i++) s.insert(ans[i]);if((int)s.size()!=n){cout<<-1<<endl;return;}for(int i=1;i<=n;i++) cout<<ans[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
33.B. Madoka and the Elegant Gift
n个长度m的01字符串
问极大矩形(全为1)是否相交
关于图形的问题,基本没做到过这类题
要想极大矩形不相交,所有的连通块必须得使矩形,那么如何判断连通块是不是矩形呢,如果不是的话,对于某个2 * 2肯定会少一块
trick:
关于图形问题
连通块
该题当作该类型ti
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=110;
int n,m;
char s[N][N];
void solve() {cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>s[i][j];}}for(int i=1;i<=n-1;i++){for(int j=1;j<=m-1;j++){map<char,int>mp;mp[s[i][j]]++;mp[s[i+1][j]]++;mp[s[i][j+1]]++;mp[s[i+1][j+1]]++;if(mp['1']==3){cout<<"No"<<endl;return;} }}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
34.B. Chat Order
trick:
可以放在前面,也可以放在后面,想到双端队列
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
deque<string>q;
int n;
void solve() {cin>>n;map<string,int>flag;for(int i=0;i<n;i++){string s;cin>>s;q.push_front(s);}for(int i=0;i<(int)q.size();i++){if(!flag[q[i]]){flag[q[i]]=1;cout<<q[i]<<endl;}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
35.A. Prefix Sum Primes
共n个数字牌,ai表示第i个数字牌上的数,1或2
将n个数字进行排序,使得前缀和为质数的个数最多
由于1的话就是就交替,偶肯定是合数,所以1一般影响不大,故对2利用好,先放一个2,为质数,再放一个1,和为3,为质数,然后一直放2,使得和始终为奇数,这样为质数的概率最大
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;map<int,int>mp;for(int i=1;i<=n;i++){int x;cin>>x;mp[x]++;}if(mp[2]&&mp[1]){cout<<2<<' '<<1<<' ';mp[2]--;mp[1]--;}for(int i=0;i<mp[2];i++) cout<<2<<' ';for(int i=0;i<mp[1];i++) cout<<1<<' '; cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
36.B. Prefix Sum Addicts
长度为n的数组a,非降序(需要构造)
给定最后k项的前缀和,构造a,问是否可以构造
最后k-1个数可以确定
前面的数的和是确定的,只要进行平均分即可,因为和一定,如果前面的数小了,后面的数就要拉大,所以平均下来,它们的最大值最小,更有机会使得整个序列非降序
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int s[N];
int n,k;
void solve() {cin>>n>>k;deque<int>q;for(int i=1;i<=k;i++) cin>>s[i];for(int i=2;i<=k;i++){q.push_back(s[i]-s[i-1]);}bool ok=true;//s[1]为正if(s[1]<0) ok=false,s[1]=-s[1];//s[1]为负//将s[1]平均分到n-(k-1)个数中int ave=s[1]/(n-k+1);int res=s[1]-s[1]/(n-k+1)*(n-k+1);//res个数是ave+1if(res) {if(ok) q.push_front(ave+1);else q.push_front((-1)*ave);}else {if(ok) q.push_front(ave);else q.push_front((-1)*ave);}for(int i=1;i<(int)q.size();i++){if(q[i]<q[i-1]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
37.B. Chloe and the sequence
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=55;
int a[N];
int n,k;
void solve() {cin>>n>>k;map<int,int>pos,mp;a[1]=1;pos[1]=1;mp[1]=1;//表示位置1上放了1for(int i=2;i<=50;i++) a[i]=a[i-1]*2+1;vector<int>ans;ans.push_back(1);for(int i=2;i<=50;i++) pos[i]=a[i]/2+1,mp[pos[i]]=i,ans.push_back(pos[i]);//位置Pos[i]上放了i
// for(int i=0;i<(int)ans.size();i++) cout<<ans[i]<<' ';
// cout<<endl;while(!mp[k]){//直到第k个位置放了数int idx=upper_bound(ans.begin(),ans.end(),k)-ans.begin()-1;//找到离k最近的上一个记录过的位置
// cout<<k<<' '<<idx<<endl;k-=ans[idx];}cout<<mp[k]<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
38.A. Almost Equal
这题就是构造一个序列,实际上还是手玩
任意两个连续n个数的和相差不超过1
相邻的首先要满足
那么对称的两个数必须相差1
trick:
1.如果对称,那么只要求出一边即可,另一边通过i+n来得到下标
2.关于数学,一定要想到奇偶位
3.关于构造序列,一定要想到奇偶位
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N<<1];
int n;
void solve() {cin>>n;if(n%2==0){cout<<"NO"<<endl;return;}for(int i=1;i<=n;i++){if(i%2==1){a[i]=2*i-1;a[i+n]=2*i;}else{a[i]=2*i;a[i+n]=2*i-1;}}cout<<"YES"<<endl;for(int i=1;i<=2*n;i++) cout<<a[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
39.B. Restricted RPS
给定三个数a,b,c,三个数的和为n
Alice出a次石头,b次布,c次剪刀
给定Bob石头剪刀布的顺序
问Alice能否赢一半,构造Alice石头剪刀布的序列
贪心,记录Bob石头剪刀布的次数,然后,尽可能,Alice都去克Bob
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
int a,b,c;
string s;
void solve() {cin>>n;cin>>a>>b>>c;cin>>s;vector<char>ans(n);for(int i=0;i<n;i++){if(s[i]=='R'&&b){ans[i]='P';b--;}else if(s[i]=='P'&&c){ans[i]='S';c--;} else if(s[i]=='S'&&a){ans[i]='R';a--;}}if(n-a-b-c<(n+1)/2){cout<<"NO"<<endl;return;}multiset<char>s;for(int i=0;i<a;i++) s.insert('R');for(int i=0;i<b;i++) s.insert('P');for(int i=0;i<c;i++) s.insert('S');for(int i=0;i<n;i++){if(ans[i]=='R'||ans[i]=='P'||ans[i]=='S') continue;ans[i]=*s.begin();s.erase(s.begin());}cout<<"YES"<<endl;for(int i=0;i<n;i++) cout<<ans[i];cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
40.B. Snow Walking Robot
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;int n=s.size();map<char,int>mp;for(int i=0;i<n;i++) mp[s[i]]++;int shuiping=min(mp['L'],mp['R']);int shuzhi=min(mp['U'],mp['D']);int ans=n-(mp['L']-shuiping+mp['R']-shuiping)-(mp['U']-shuzhi+mp['D']-shuzhi);if(shuiping==0||shuzhi==0){if(shuiping==0&&shuzhi==0) cout<<0<<endl;else if(shuiping==0){cout<<2<<endl;cout<<"UD"<<endl;}else{cout<<2<<endl;cout<<"LR"<<endl;}return;}cout<<ans<<endl;for(int i=0;i<shuiping;i++) cout<<'R';for(int i=0;i<shuzhi;i++) cout<<'D';for(int i=0;i<shuiping;i++) cout<<'L';for(int i=0;i<shuzhi;i++) cout<<'U';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
41.B. Morning Jogging
trick:
1.如果报错的话,那么就一块一块进行注释,哪一块注释后可以运行了就说明哪一块有问题
2.如果想把一些数按顺序放在数组里,可以先将这些数放在线性容器里,比如multiset中,然后遍历数组,将*s.begin()存放进去即可,然后s.erase(s.begin())
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=110;
int b[N][N],a[N][N];
int n,m;
multiset<int>s[N];
void solve() {cin>>n>>m;memset(a,0,sizeof a);for(int i=1;i<=n;i++) s[i].clear();vector<int>ans;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){int x;cin>>x;b[i][j]=x;ans.push_back(x);}}map<int,int>mp,mmp;sort(ans.begin(),ans.end());for(int i=0;i<m;i++){mmp[ans[i]]++;if(!mp[ans[i]]) mp[ans[i]]=i+1;//分别将前m个数放在m条道路上 }for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(mp[b[i][j]]&&mmp[b[i][j]]) a[i][mp[b[i][j]]]=b[i][j],mp[b[i][j]]++,mmp[b[i][j]]--;else s[i].insert(b[i][j]);}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(a[i][j]) continue;a[i][j]=*s[i].begin();s[i].erase(s[i].begin());}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cout<<a[i][j]<<' ';}cout<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
42.B. Square Filling
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=55;
char a[N][N];
char b[N][N];
int n,m;
int x[N*N],y[N*N];
void solve() {cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>a[i][j];b[i][j]='0';}}int cnt=0;for(int i=1;i<=n-1;i++){for(int j=1;j<=m-1;j++){if(a[i][j]=='1'&&a[i+1][j]=='1'&&a[i][j+1]=='1'&&a[i+1][j+1]=='1'){cnt++;x[cnt]=i;y[cnt]=j;b[i][j]=b[i+1][j]=b[i][j+1]=b[i+1][j+1]='1';}}}for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){if(a[i][j]!=b[i][j]){cout<<-1<<endl;return;}}}cout<<cnt<<endl;for(int i=1;i<=cnt;i++) cout<<x[i]<<' '<<y[i]<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
43.B. Easter Eggs
如果是7的倍数,就一直循环
1 2 3 4 5 6 7
如果余1,最后一个数放4
如果余2,最后两个数放34
如果余3,最后三个数放234
余4,放1234
余5,放12345
余6,放123456
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s="ROYGBIV";//0到6
string s1="G";
string s2="YG";
string s3="OYG";
string s4="ROYG";
string s5="ROYGB";
string s6="ROYGBI";
void solve() {cin>>n;int circle=n/7;for(int i=0;i<circle;i++) cout<<s;if(n%7==1) cout<<s1;else if(n%7==2) cout<<s2;else if(n%7==3) cout<<s3;else if(n%7==4) cout<<s4;else if(n%7==5) cout<<s5;else if(n%7==6) cout<<s6;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
44.B. Omkar and Heavenly Tree
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N],c[N];
int n,m;
void solve() {cin>>n>>m;map<int,int>mp;for(int i=0;i<m;i++){cin>>a[i]>>b[i]>>c[i];mp[b[i]]++;}for(int i=1;i<=n;i++){if(mp[i]==0){for(int j=1;j<=n;j++){if(i==j) continue;cout<<i<<' '<<j<<endl;}return;}}int idx;for(int i=1;i<=n;i++){if(mp[i]==1){idx=i;break;}}int l,r;for(int i=1;i<=n;i++){if(b[i]==idx){l=a[i];r=c[i];break;}}for(int i=1;i<=n;i++){if(i==l||i==r||i==idx) continue;cout<<idx<<' '<<i<<endl;}if(idx>=3){cout<<1<<' '<<l<<endl;cout<<2<<' '<<r<<endl;}else{cout<<n-1<<' '<<l<<endl;cout<<n<<' '<<r<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
45.C. Dishonest Seller
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=2e5+10;
int a[N],b[N];
int n,k;
void solve() {cin>>n>>k;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];int cnt=0;//现在便宜的有几个map<int,int>mp;//标记哪些是买afor(int i=1;i<=n;i++){if(a[i]<b[i]) cnt++,mp[i]=1;}if(cnt>=k){int ans=0;for(int i=1;i<=n;i++){ans+=min(a[i],b[i]);}cout<<ans<<endl;return;}k-=cnt;//还要补足几个vector<PII>ans;for(int i=1;i<=n;i++){if(mp[i]) continue;ans.push_back({a[i]-b[i],i});}sort(ans.begin(),ans.end());for(int i=0;i<k;i++) mp[ans[i].second]=1;int res=0;for(int i=1;i<=n;i++){if(mp[i]) res+=a[i];else res+=b[i];}cout<<res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
46.B. Build the Permutation
构造a个波峰,b个波谷
全排列
无解输出-1
n为2肯定无解
n为3,要么造一个波峰,要么造一个波谷,和为1
n为4,一个波峰,一个波谷,和为2
n为5,一个波峰两个波谷,或者两个波峰一个波谷,和为3
n为6,除了开头和结尾,中间都可是波峰或者波谷,和为4
波峰波谷是一起出现的,看最多可以有几个波峰,有几个波谷 ,如果不需要那么多,那么后面的就都有序、
波峰和波谷个数最多差1
奇偶分位
苦于没有好的构造方法
trick:
1.构造波峰和波谷:升降升降(先有波峰再有波谷,波峰只会比波谷多不会少)…或者降升降升(先有波谷再有波峰,波谷只会比波峰多不会少)…具体做法将所有数排序,双指针,放最小,放最大,放次小,放次大,…这样就能构造波峰和波谷了,然后如果不想造了,就直接一直放一边的就行了
2.1和0的交替,可以利用异或,flag^=1
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,a,b;
void solve() {cin>>n>>a>>b;if(a+b>n-2||abs(a-b)>1){cout<<-1<<endl;return;}int l=1,r=n,flag=(a>b);for(int i=1;i<=n;i++){if(flag){cout<<l<<' ';l++;}else{cout<<r<<' ';r--;}if(i<=a+b) flag^=1;}cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
47.B. Diameter of Graph
trick:
图论,特判n为1
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m,k;
void solve() {cin>>n>>m>>k;if(n==1){if(m) cout<<"No"<<endl;else if(k>1) cout<<"Yes"<<endl;else cout<<"No"<<endl;return;}if(m<n-1) cout<<"No"<<endl;else if(m<n*(n-1)/2){if(k-1>2) cout<<"Yes"<<endl;else cout<<"No"<<endl;}else if(m==n*(n-1)/2){if(k-1>1) cout<<"Yes"<<endl;else cout<<"No"<<endl;}else if(m>n*(n-1)/2) cout<<"No"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
48.A. Vladik and flights
只有0和1,如果既有0也有1,肯定有0和1挨着,那么最多花费1
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,a,b;
string s;
void solve() {cin>>n>>a>>b;cin>>s;s=' '+s;if(s[a]==s[b]){cout<<0<<endl;return;}map<char,int>mp;for(int i=1;i<=n;i++) mp[s[i]]++;if(mp['0']==0||mp['1']==0) cout<<0<<endl;else cout<<1<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
49.Problem - 264A - Codeforces
trick:
可以利用vector的emplace将元素插在中间 ,但是时间复杂度为O(n)
另外,vector的end()是最后一个元素的下一个位置的迭代器,最后一个元素的迭代器为 --ans.end()
//超时代码
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;int n=s.size();vector<int>ans;ans.emplace_back(1);auto it=ans.begin();if(s[0]=='l') ans.emplace(ans.begin(),2),it=ans.begin();else ans.emplace_back(2),it=ans.end()-1;for(int i=1;i<n-1;i++){if(s[i]=='l') it=ans.emplace(it,i+2);else {if(it==ans.end()-1) ans.emplace_back(i+2),it=ans.end()-1;else it++,it=ans.emplace(it,i+2);}
// for(auto v:ans) cout<<v<<' ';
// cout<<endl;
// cout<<*it<<endl;}for(int i=0;i<(int)ans.size();i++) cout<<ans[i]<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
当向右移动时,石头被放在松鼠的左边,然后后面也只能在右边放石头了,所以就可以输出了
当向左移动时,左边还是可以放的,于是先不输出,由于先放的石头后输出,于是用栈存储
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;stack<int>stk;for(int i=0;i<(int)s.size();i++){if(s[i]=='r') cout<<i+1<<endl;else stk.push(i+1);}while(stk.size()){cout<<stk.top()<<endl;stk.pop();}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
50.B. Equalize by Divide
trick:
数据小,直接暴力
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=110;
int n;
struct node{int a,idx;bool operator<(const node &W)const{return a<W.a;}
}q[N];
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>q[i].a,q[i].idx=i;sort(q+1,q+1+n);bool ok=true;for(int i=2;i<=n;i++){if(q[i].a!=q[i-1].a){ok=false;break;}}if(ok){cout<<0<<endl;return;}if(q[1].a<=1){cout<<-1<<endl;return;}vector<PII>ans;while(1){sort(q+1,q+1+n);bool ok=true;for(int i=2;i<=n;i++){if(q[i].a!=q[i-1].a){ok=false;break;}}if(ok) break;for(int j=2;j<=n;j++){while(q[j].a>q[1].a){q[j].a=q[j].a/q[1].a+(q[j].a%q[1].a!=0);ans.push_back({q[j].idx,q[1].idx});}}}cout<<ans.size()<<endl;for(auto v:ans) cout<<v.first<<' '<<v.second<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
51.B. New Year and the Treasure Geolocation
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1010;
int x[2*N],y[2*N];
int n;
void solve() {cin>>n;for(int i=1;i<=2*n;i++) cin>>x[i]>>y[i];sort(x+1,x+1+2*n);sort(y+1,y+1+2*n);cout<<x[1]+x[2*n]<<' '<<y[1]+y[2*n]<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}
52.B. Kuroni and Simple Strings
贪心,双指针
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;int n=s.size();vector<int>a,b;int i=0,j=n-1;while(i<j){if(s[i]=='('&&s[j]==')'){a.push_back(i+1);b.push_back(j+1);i++,j--;}else if(s[i]=='('&&s[j]=='(') j--;else if(s[i]==')'&&s[j]=='(') i++,j--;else if(s[i]==')'&&s[j]==')') i++;}reverse(b.begin(),b.end());if(a.size()==0) cout<<0<<endl;else{cout<<1<<endl;cout<<a.size()*2<<endl;for(auto v:a) cout<<v<<' ';for(auto v:b) cout<<v<<' ';cout<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}