文章目录
- 1.[B. Random Teams](https://codeforces.com/contest/478/problem/B)
- 2.[D. Anti-Sudoku](https://codeforces.com/problemset/problem/1335/D)
- 3.[B. Trouble Sort](https://codeforces.com/problemset/problem/1365/B)
- 4.[Problem - 1401C - Codeforces](https://codeforces.com/problemset/problem/1401/C)
- 5.[Problem - 1367C - Codeforces](https://codeforces.com/problemset/problem/1367/C)
- 6.[C. Coin Rows](https://codeforces.com/problemset/problem/1555/C)
- 7.[B. Nastia and a Good Array](https://codeforces.com/problemset/problem/1521/B)
- 8.[A1. Prefix Flip (Easy Version)](https://codeforces.com/problemset/problem/1381/A1)
- 9.[D. Number into Sequence](https://codeforces.com/problemset/problem/1454/D)
- 10.[C1. Pokémon Army (easy version)](https://codeforces.com/contest/1420/problem/C1)
- 11.[C. Closest Cities](https://codeforces.com/contest/1922/problem/C)
- 12.[B. Road Construction](https://codeforces.com/problemset/problem/330/B)
- 13.[A. Di-visible Confusion](https://codeforces.com/contest/1603/problem/A)
- 14.[C. Matching Numbers](https://codeforces.com/problemset/problem/1788/C)
- 15.[D. Co-growing Sequence](https://codeforces.com/problemset/problem/1547/D)
- 16.[B. Simple Game](https://codeforces.com/problemset/problem/570/B)
- 17.[B. Swap and Delete](https://codeforces.com/contest/1913/problem/B)
- 18.[C. Game with Multiset](https://codeforces.com/contest/1913/problem/C)
- 19.[D. Ice Cream Balls](https://codeforces.com/problemset/problem/1862/D)
- 20.[A. Knapsack](https://codeforces.com/problemset/problem/1446/A)
- 21.[C. Parity Shuffle Sorting](https://codeforces.com/problemset/problem/1733/C)
- 22.[A1. Make Nonzero Sum (easy version)](https://codeforces.com/problemset/problem/1753/A1)
- 23.[C. Elemental Decompress](https://codeforces.com/problemset/problem/1768/C)
- 24.[D. Twist the Permutation](https://codeforces.com/problemset/problem/1650/D)
- 25.[D. Twist the Permutation](https://codeforces.com/problemset/problem/1650/D)
- 26.[C. Divisor Chain](https://codeforces.com/contest/1864/problem/C)
- 27.[C. Permutation Operations](https://codeforces.com/problemset/problem/1746/C)
- 28.[C. Ehab and a Special Coloring Problem](https://codeforces.com/problemset/problem/1174/C)
- 29.[B. Gardener and the Array](https://codeforces.com/problemset/problem/1775/B)
- 30.[C. Ice and Fire](https://codeforces.com/problemset/problem/1774/C)
- 31.[A. Qingshan Loves Strings 2](https://codeforces.com/contest/1889/problem/A)
- 32.[B. New Year's Eve](https://codeforces.com/problemset/problem/912/B)
- 33.[C. Insert Zero and Invert Prefix](https://codeforces.com/contest/1839/problem/C)
- 34.[B. Binary String Constructing](https://codeforces.com/problemset/problem/1003/B)
- 35.[A. Packets](https://codeforces.com/problemset/problem/1037/A)
- 36.[C. Insert and Equalize](https://codeforces.com/problemset/problem/1902/C)
- 37.[B. Vasya and Isolated Vertices](https://codeforces.com/problemset/problem/1065/B)
- 38.[A. Fill in the Matrix](https://codeforces.com/problemset/problem/1868/A)
- 39.[B. Bit Flipping](https://codeforces.com/problemset/problem/1659/B)
- 40.[A. Oh Those Palindromes](https://codeforces.com/problemset/problem/1063/A)
- 41.[C. Salyg1n and the MEX Game](https://codeforces.com/problemset/problem/1867/C)
- 42.[C. Labs](https://codeforces.com/problemset/problem/1236/C)
- 43.[B. Painting Pebbles](https://codeforces.com/problemset/problem/509/B)
- 44.[B. Sonya and Exhibition](https://codeforces.com/problemset/problem/1004/B)
- 45.[A. Difference Row](https://codeforces.com/problemset/problem/347/A)
- 46.[B. Neko Performs Cat Furrier Transform](https://codeforces.com/problemset/problem/1152/B)
- 47.[C. Dividing the numbers](https://codeforces.com/problemset/problem/899/C)
- 48.[A. Lucky Permutation Triple](https://codeforces.com/problemset/problem/303/A)
- 49.[B. Students in Railway Carriage](https://codeforces.com/problemset/problem/962/B)
- 50.[B. Vika and Squares](https://codeforces.com/problemset/problem/610/B)
1.B. Random Teams
n个人,分成m组,每个组至少分一个人
靠一些直觉,分别对最大和最小进行思考
最大:除一组外其它全为1
最小:平均分,那么怎样平均分呢?
trick:平均分的做法:先每组分配n/m,如果n%m不为0的话,那么取n%m个加1
主要是对平均分这一技巧的掌握
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;int t=n-m+1;int maxn=t*(t-1)/2;int x=n/m;int y=n%m;int minn=(x+1)*x/2*y+x*(x-1)/2*(m-y);cout<<minn<<' '<<maxn<<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.D. Anti-Sudoku
9 * 9的数独,最多改掉9个数,使得每行,每列,每个3 * 3都至少有两个相同的元素(题目说一定有解)
直觉是修改的数值每行每列都不一样,比如(1,1),(2,2),…(9,9)通过样例来验证,是适用的
那么具体改成什么值呢?由于原本这是一个数独,即每行1到9不重复,没列1到9不重复,3 * 3中1到9不重复,所以只要改成不一样的就会造成它所在行,列,3 * 3会出现至少两个相同的元素,发现这样不行,还得再改改,这样的话很多3 * 3还是没有改掉,所以还要考虑3 * 3
trick:直接对以下进行修改
(1,1) (2,4)(3,7)
(4,2) (5,5)(6,8)
(7,3) (8,6)(9,9)当然,这里也有一定的技巧,就是横纵坐标分别放到dx数组和dy数组里,直接遍历即可,而不是写9个if
#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
char a[10][10];
int dx[9]={1,2,3,4,5,6,7,8,9};
int dy[9]={1,4,7,2,5,8,3,6,9};
void solve() {for(int i=1;i<=9;i++){for(int j=1;j<=9;j++){cin>>a[i][j];}}for(int i=0;i<9;i++){int x=dx[i],y=dy[i];if(a[x][y]=='9') a[x][y]='1';else a[x][y]=(char)(a[x][y]+1);}for(int i=1;i<=9;i++){for(int j=1;j<=9;j++){cout<<a[i][j];}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. Trouble Sort
n个整数
ai表示值,bi表示类型
操作:选择两个类型不同的数,交换它们
操作不限次数,使得非降序,问是否可以
只要分别将两种类型的升序,然后合并在一起,再检验是否非降序
难点在于怎么实现:其实很简单,就是先把两种类型的数分别放在不同的multiset里,然后依次按照类型赋值回去即可
由于multiset中的erase是删掉的是与之相同的所有元素,我要删第一个,不知道怎么删,所以改成小根堆
样例一直错,然后才发现题目读错了,全都白分析了,不对,题目读对了,但是在分析的时候错乱了,刚好把题意记反了,记成交换相同类型的了
这告诉我们一个道理:先别急着敲代码,先看看想出的方法是不是对样例适用,应该至少保证样例全都适用
后面想的是进行一个类似于冒泡排序,就是如果类型不同且大小逆序就互换,但是时间复杂度肯定不允许
trick:操作是对某个数进行交换且操作次数不限,那么看两两交换是否可以扩展到更多数的交换,这里只要有0和1的交错,那么就全部都可以随意交换
#include<bits/stdc++.h>
#define endl '\n'
//#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++) cin>>b[i];bool flag=true;int cnt_0=0,cnt_1=0;if(b[1]==0) cnt_0++;else cnt_1++;for(int i=2;i<=n;i++){if(a[i-1]>a[i]) flag=false;if(b[i]==0) cnt_0++;else cnt_1++;}if(flag){cout<<"Yes"<<endl;return;}if(cnt_0==0||cnt_1==0) cout<<"No"<<endl;else cout<<"Yes"<<endl;
}
int main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
4.Problem - 1401C - Codeforces
长度为n的数组
ai为正整数
操作:选择两个数,如果它们的最大公因数等于整个数组最小的那个就可以交换
操作次数不限
要求使a非降序
想想是否可以扩展一下交换
数组中最小的数在数组给定之后就已经确定下来了
至此都没什么解题的突破口
trick:
这类题都是有一般套路的
这类题的特征:操作是满足某性质的两个数可以交换,操作次数不限,目标是排好序
思考方向为是否有中间量可以作为交换的媒介,两数通过中间的媒介进行交换,某个数如果和中间量有联系的话,那么就可以和其它任意和中间量有联系的进行交换,从而达到自由移动
另外一种思考的方向是,我们要的结果是确定的,所以先排个序,结果确定下来,然后看位置不正确的数是否全部都可以自由移动
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int n;
int gcd(int a,int b){if(b==0) return a;return gcd(b,a%b);
}
void solve() {cin>>n;int minn=2e9;for(int i=1;i<=n;i++) cin>>a[i],b[i]=a[i],minn=min(minn,a[i]);sort(b+1,b+1+n);for(int i=1;i<=n;i++){if(a[i]!=b[i]){if(gcd(a[i],minn)!=minn){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;
}
5.Problem - 1367C - Codeforces
1到n张桌子
长度为n的01字符串,1代表餐桌有人,0代表没人
保证给定字符串中任意两个1相差距离大于k
然后问最多将几个0变成1使得任意两个1相差距离仍然大于k
如果当前字符为0的话,查找往后k个距离内是否有1,如果没有就可以将该位置的0变为1,并跳到i+k的位置
如果当前字符为1的话,那么直接跳到i+k的位置
trick:
当出现最的时候,一种思路就是贪心:最好是怎么样,实在没办法才舍弃
贪心其中一种方向就是从前往后遍历,一边遍历一边贪,若可以将该位置的0变成1则变,尽可能多
#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;
string s;
void solve() {cin >> n >> k;cin >> s;vector<int>pos;//存放1的位置for (int i = 0; i < n; i++) {if (s[i] == '1') pos.push_back(i);}int ans = 0;for (int i = 0; i < (int)s.size(); i++) {if (s[i] == '0') {int pos1 = upper_bound(pos.begin(), pos.end(), i) - pos.begin();if (pos1 >= 0 && pos1 < (int)pos.size()) {if(pos[pos1] - i > k){ans++;i = i + k;}}else{ans++;i=i+k;}} else {i = i + k;}}cout << ans << 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.C. Coin Rows
矩阵
共m列,2行
aij表示硬币数量
博弈
起点为(1,1),终点为(2,m)
操作:往右移动一格或者往下移动一格
最终分数为Bob获取的硬币数量
Alice从起点走到终点,拿取硬币
Alice想使得分数最小
Bob再走,想使分数最大
两者独立,互不相关
有关于数学数字,归结为数学敏感,手玩,找规律
trick:
1.有关于数学数字,归结为数学敏感,手玩,找规律
2.不要乱加特判,情况虽然简单,但不要也开始就加上,先看一般情况能否覆盖特殊情况,如果可以覆盖,那么它就不是特殊情况,加了特判反而容易错
这边对m=2时特判反而错了,属于画蛇添足了
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[3][N];
int last[N],pre[N];
int m;
void solve() {cin>>m;for(int i=1;i<=2;i++){for(int j=1;j<=m;j++){cin>>a[i][j];}}//特判if(m==1){cout<<0<<endl;return;}//预处理第一行的后缀和last[m]=a[1][m];for(int i=m-1;i>=1;i--) last[i]=last[i+1]+a[1][i];//预处理第二行的前缀和pre[1]=a[2][1];for(int i=2;i<=m;i++) pre[i]=pre[i-1]+a[2][i];//遍历int ans=2e9;ans=min(ans,pre[m-1]);ans=min(ans,last[2]);for(int i=1;i<=m-2;i++){//i为第二行前缀的长度int len=m-i-1;//第一行后缀的长度ans=min(ans,max(pre[i],last[m+1-len]));}cout<<ans<<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.B. Nastia and a Good Array
长度为n的数组a,均为正整数
good数组:所有相邻两个数最大公因数均为1
操作:选择任意两个数,将其中一个数变成小的那个数min,另一个数随便变成一个大于等于min的数
最多操作n次,可以证明一定可以实现,变成good数组
输出操作
有一个很重要的性质:相差为1的两个数的最大公因数肯定为1,这在之前做题的时候做到过
把相邻的两个数变成相差为1的两个数,n次绝对够了,而且是刚好n-1次,想错了,没有想到修改后面就把前面的数给改了
为了避免修改前面的数会把后面的数给改掉,需要按升序的顺序来,这样ok了,这样还是不对,因为虽然排序了,但是原本的顺序是没有打乱的
而升序还有一个原因----该题与顺序无关,先排个序
这题最小的数可以保留,然后其它都可以变成大于等于最小数的任何数,那么可以奇数位全放最小数,偶数位全放最小数+1,或者偶数位全放最小数,奇数位全放最小数+1
trick:
1.相差为1的两个数的最大公因数肯定为1,这是之前做题做到过的,一个很重要很关键的性质,但是这里还要再拓展一下,题目要求是任意相邻两个数的最大公因数均为1,可能会想到每次都加1,呈现一个升序的状态,但是也可以减1,加1,这样就分为奇数位和偶数位,奇数位均相同,偶数位均相同,奇数位上的数和偶数位上的数差1
2.将一个数和另一个数联系起来,输出的时候输出这两个数,包括上次做到的输出生成树一条边的两个端点,看是否可以其中一个数就固定下来
3。挖掘操作(动作)的性质,想想哪些是一定的,首先做什么是一定的,在这里是最小的那个数一定是可以保留的
#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];int pos=1,minn=a[1];for(int i=2;i<=n;i++){if(a[i]<minn){minn=a[i];pos=i;}}cout<<n-1<<endl;if(pos%2==1){for(int i=1;i<=n;i++){if(pos==i) continue;if(i%2==0) cout<<pos<<' '<<i<<' '<<a[pos]<<' '<<a[pos]+1<<endl;else cout<<pos<<' '<<i<<' '<<a[pos]<<' '<<a[pos]<<endl;}}else{for(int i=1;i<=n;i++){if(pos==i) continue;if(i%2==0) cout<<pos<<' '<<i<<' '<<a[pos]<<' '<<a[pos]<<endl;else cout<<pos<<' '<<i<<' '<<a[pos]<<' '<<a[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;
}
8.A1. Prefix Flip (Easy Version)
长度为n的数组a和数组b,01串
操作:取长度为len的前缀,将0变成1,将1变成0,然后翻转
将字符串a变成b,需操作几次(最多3*n次,一定有解)
3次操作:翻转前i个字符,翻转第1个字符,再翻转前i个字符,可以将第i个字符进行翻转–这个规律真的很难发现,通过手玩也很难,可以记一下这个规律
trick:
1.一定有解,最多3*n次–>肯定可以构造出一种万能的通用的解,因为最多n个字符,然后通过3可以猜测每3次就可以将一个字符翻转
2.非常通用的一种方法–手玩
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string a,b;
void solve() {cin>>n;cin>>a>>b;vector<int>ans;for(int i=0;i<n;i++){if(a[i]!=b[i]){ans.push_back(i+1);ans.push_back(1);ans.push_back(i+1);}}cout<<ans.size()<<' ';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;
}
9.D. Number into Sequence
构造序列,每个数大于1,所有数乘积为n,后一个数是前一个数的倍数,长度要最大(一定有解)
分解质因数,将出现次数最多的质因数(假设k个)输出k-1个,剩下一个和其它乘积
trick:
数学知识:每个合数都可以写成几个质数相乘的形式(唯一),其中每个质数都是这个合数的因数,把一个合数用质因数相乘的形式表示出来,叫做分解质因数,这样得到的分解因数的序列一定是最长的,因为质数不能再分解了,然后将质因数乘积合成一个因数,这样可以有办法满足题意得最长序列
分解质因数的模板:试除法
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;int m=n;vector<int>ans;map<int,int>mp;for(int i=2;i<=n/i;i++){if(n%i==0){ans.push_back(i);while(n%i==0) {n/=i;mp[i]++;}}}if(n>1){ans.push_back(n);mp[n]++;}
// for(int i=0;i<(int)ans.size();i++) cout<<ans[i]<<' '<<mp[ans[i]]<<endl;int maxn=mp[ans[0]];int maxi=ans[0];for(int i=1;i<(int)ans.size();i++){if(maxn<mp[ans[i]]){maxn=mp[ans[i]];maxi=ans[i];}}cout<<maxn<<endl;for(int i=0;i<maxn-1;i++) cout<<maxi<<' ';cout<<m/(int)pow(maxi,maxn-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.C1. Pokémon Army (easy version)
n个神奇宝贝
ai表示神奇宝贝的实力(均不同)
按顺序挑选神奇宝贝,加减交替,得到该支军队的实力,求出最大兵力
开头和结尾肯定是要加的(让个数为奇数,不然后面减一个肯定变小)
通过手玩造样例发现,比如8 7 6 5 4,如果选择8,7,6,5,10,那么最终结果是8-7+6-5+10=12,而选择8,5,10,则为8-5+10=13,所以一加一减选择一个最大的和最小的,它其实就是一个波峰波谷,每次都选择波峰和波谷即可
trick:
1.手玩–造样例2.找波峰和波谷
波峰:a[i] > a[i - 1] && a[i] > a[i + 1]
波谷:a[i] < a[i - 1] && a[i] < a[i + 1]
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N = 3e5 + 10;
int a[N];
int n, q;
void solve() {cin >> n >> q;for (int i = 1; i <= n; i++) cin >> a[i];//找波峰和波谷bool flag = 1; //1表示要找波峰,0表示要找波谷vector<int>ans;if (a[1] > a[2]) ans.push_back(a[1]), flag = 0;for (int i = 2; i <= n - 1; i++) {if (flag == 1) { //找波峰if (a[i] > a[i - 1] && a[i] > a[i + 1]) ans.push_back(a[i]), flag = 0;} else if (flag == 0) { //找波谷if (a[i] < a[i - 1] && a[i] < a[i + 1]) ans.push_back(a[i]), flag = 1;}}if (a[n] > a[n - 1]) ans.push_back(a[n]);int res = 0;for (int i = 0; i < (int)ans.size() - 1; i += 2) {res += ans[i] - ans[i + 1];}res += ans[ans.size() - 1];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;
}
11.C. Closest Cities
长度为n的数组a,数的范围为[0,1e9]
共m行询问,每次给定两个整数x和y,计算从城市x到城市y需要花费最少的硬币数
共m座城市,位于ai,按升序排列
对于一座城市,离它最近的城市是唯一的
操作:从城市x到y,花费它们距离的金币或者到离x最近的城市(唯一的)花费1金币
x和y在两个方向,x朝着y方向走,一直走最近,直到最近的是往回走,千万不能走回头路,此时直接花费距离金币到y
并查集不可行,并查集不能有环,那就用两个并查集,往右的和往左的,但是这样是错的,因为并不是走到并查集的头就断了,可以先利用城市距离过去再继续走最近城市
由于不能走回头路,所以我们利用一个前缀和以及后缀和就可以了,如果可以走最近城市那么就加1,否则就加两城市之间的距离
不能走回头路,然后如果能走最近城市就走最近城市,如果不能走就花费距离金币,这样花费代价是最小的
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 pre[N],last[N];
int n,m;
void solve() {cin>>n;memset(pre,0,sizeof pre);memset(last,0,sizeof last);for(int i=1;i<=n;i++) cin>>a[i];//前缀和for(int i=1;i<n;i++){if(i==1) pre[i+1]=pre[i]+1;else{if(abs(a[i]-a[i+1])<abs(a[i]-a[i-1])) pre[i+1]=pre[i]+1;else pre[i+1]=pre[i]+abs(a[i]-a[i+1]);}}//后缀和for(int i=n;i>1;i--){if(i==n) last[i-1]=last[i]+1;else{if(abs(a[i]-a[i-1])<abs(a[i]-a[i+1])) last[i-1]=last[i]+1;else last[i-1]=last[i]+abs(a[i]-a[i-1]);}}cin>>m;while(m--){int x,y;cin>>x>>y;if(x<y) cout<<pre[y]-pre[x]<<endl;else cout<<last[y]-last[x]<<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. Road Construction
一共有n座城市
建设道路(无向图),对于一座城市,到任何其它城市最多穿越两条道路
m对城市不能建设道路
一定有解
输出最少建设多少条道路,输出具体建设情况
记录不能建设道路的情况,记录每个城市不能用到的次数,次数最少的x去匹配那些可以和它连的,对于不能和x连的,看是否可以连到x的一级分支上
数据比较小,可以选择暴力
trick:
1.当两者需要联系起来时输出,往往其中一个可以定死,这是之前总结过的,在这里是解题的关键
2.数据比较小,直接暴力
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1010;
int n,m;
bool st[N][N];
void solve() {cin>>n>>m;map<int,int>mp;memset(st,false,sizeof st);for(int i=0;i<m;i++){int x,y;cin>>x>>y;st[x][y]=st[y][x]=true;mp[x]++;mp[y]++;}int minn=2e9,mini=-1;for(int i=1;i<=n;i++){if(minn>mp[i]){minn=mp[i];mini=i;}}cout<<n-1<<endl;vector<int>ans;vector<int>res;for(int i=1;i<=n;i++){if(i==mini) continue;if(!st[mini][i]) cout<<mini<<' '<<i<<endl,res.push_back(i);else ans.push_back(i);}for(int i=0;i<(int)ans.size();i++){//枚举和mini不能相连的for(int j=0;j<(int)res.size();j++){if(!st[ans[i]][ans[j]]) cout<<ans[i]<<' '<<ans[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;
}
13.A. Di-visible Confusion
长度为n的数组a(数的范围[1,1e9])
操作:选择一个索引i,保证ai不是i+1的倍数,删掉ai
直到序列为空、
问是否可以删除整个序列
手玩
要想删掉第一个数,那么第一个数就不能是2的倍数(第一个数要看的索引是固定的,是2)
如果全部是奇数,那么就一直删第一个数
删掉后面不会影响前面,删掉前面会影响后面
先把能删的都删掉,然后剩下的就都是不能删的了
然后只要把第一个删掉,所有都错一位,这样全部都变成能删的了,然后从后往前一个一个删
综上所述,如果第一个数是奇数的话, 那么Yes,否则N
全错了,这样错位下来不一定,比如说30,既是5的倍数,又是6的倍数
该题的做法是假设第i个数前面的数都可以被删掉,那么第i个数只要在2到i+1中有一个不是因数,也可以被删掉,如果有一个数,2到i+1全部都是它的因数的话,那么它不可能被删掉
由于2乘以3,一直乘到大概23就超过1e9了,所以最多判断22个数,这样双重循环是不会超时的
trick:
1.整除想到因子
2.对于删数问题,肯定是一位一位删,不可能一下子全部都删掉,其中一个思考的方向是假设前面的数全部都可以删掉,然后如何删掉该数
3.如果x是很多数的倍数,那么x也是它们最小公倍数的倍数,判断x是否是很多数的倍数,只需看是否是它们最小公倍数的倍数,一个一个判断是否是因子,那么阶乘级别的,判断次数不会很多,就会有一个不是因子
#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];bool flag=true;for(int i=1;i<=n;i++){bool ok=false;for(int j=i+1;j>=2;j--){if(a[i]%j!=0){ok=true;break;}}flag&=ok;}if(flag) cout<<"Yes"<<endl;else 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;
}
14.C. Matching Numbers
给定n
然后让1到2*n两两匹配,使得它们的和按顺序分别是加1
它们所有的和是固定的,然后每个数都差1,求和就是等差数列,等差数列一共n个数
可以列出一个式子,假设第一个和为x,那么(x+x+n-1) * n /2=(1+2 * n)* 2 * n /2
得到x=(3 * n+3)/2
然后n个和即为,x,x+1,…x+n-1
通过手玩发现构造规律:将1到2n分成两半,左半边的x-(n+1)到n分别按次序和右半边相加,然后右半边剩下的按次序和左半半剩下的相加即可
trick:
突破口在于和是固定的,一旦找到某个是不变的,它就是题目的突破口
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;if((3*n+3)%2){cout<<"No"<<endl;return;}int x=(3*n+3)/2;//第一个和int pos=x-(n+1);cout<<"Yes"<<endl;int idx;for(int i=n+1,j=pos;j<=n;i++,j++){cout<<i<<' '<<j<<endl;idx=i;}for(int i=idx+1,j=1;i<=2*n;i++,j++){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;
}
15.D. Co-growing Sequence
长度为n的数组x(数的范围[0,1e9])
增长序列:任意i,ai & ai+1 =ai(n为1也是增长序列)
构造序列b,ai^bi=ci,使得c是增长序列(这里ai并不是增长序列)
求b的字典序最小
b 1 b_1 b1可以任意,直接取为最小,为0
即( a i a_i ai$b_i$)&($a_{i+1}$ b i + 1 b_{i+1} bi+1)= a i + 1 a_{i+1} ai+1^ b i + 1 b_{i+1} bi+1
得到 a i a_i ai$b_i$中1所在的位,$a_{i+1}$ b i + 1 b_{i+1} bi+1也都是1,由于 a i + 1 a_{i+1} ai+1已知,那么只需要把 a i + 1 a_{i+1} ai+1中应该为1的位但不是1的改成1即可作为b,其它位都为0,这样必要的1有了,其它都为0,字典序最小
trick:
1.按位异或,一个重要的性质是,相同为0,即aa=0,从而得到abb=a,即a=abb,如果题目要求构造一个数使得该数和给定数a异或起来满足要求,可以先把满足要求的数x确定下来,然后需要构造的数y=xa(因为ay=x,所以aya=xa)
如果两者异或起来为1的话,那么两者不同,由于二进制只有0和1,所以两者其中一个是0,另一个是1,换句话说,两者刚好相反,互补
2.按位与,如果a & b =a,那么a中1所在的位,b也都是1
3.按位或,如果a中为1的位,b都想要为1,同时保留b中原有的1,那么b或上a,相当于将两者为1的位都并起来为x,如果p^q为x的话,除了两者都为0的位,其它位都互补
# include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N],b[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];b[1]=0;for(int i=2;i<=n;i++){int x=a[i-1]^b[i-1];b[i]=(x|a[i])^a[i];///a[i]^b[i]=x|a[i]}for(int i=1;i<=n;i++) cout<<b[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;
}
16.B. Simple Game
在1到n中选择一个数
两个人M和A博弈,对于随机整数c,谁选的数离c近,谁就赢,如果距离相同,M赢
M数字已知,要想让A赢的概率最大,A应该选择哪个数
根据样例,看M在左半边还是右半边
选择数字M-1或者M+1
M-1和n-(M+1)+1=n-M比,哪个大选哪个
坑点:
M-1和M+1超出选数的范围了
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;if(m-1>=n-m&&m-1>=1) cout<<m-1<<endl;else if(m-1<n-m&&m+1<=n) cout<<m+1<<endl;else cout<<m<<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. Swap and Delete
1000分的题,但还是归到这来,因为这题和之前做过的题联系起来了,比较有重大意义
给定一个01串s
操作:删除一个字符,花费1金币或者免费交换一对字符
次数不限
使得ti不等于si对于整个t(可以为空)
问最少花费多少金币
两个一样的序列进行0和1的匹配,分别统计0和1的数量,从头开始遍历,一直到不能匹配
trick:
1.两两可以任意交换–可以随便排序
2.两两匹配问题
分为三种
第一种是序列内部进行两两匹配,比如说最小和最大匹配,次小和次大匹配
第二种是两个不同的序列进行匹配,比如说每次匹配差值最大的,要么序列a的最大和序列b的最小匹配,要么序列a的最小和序列b的最大匹配
第三种是两个一样的序列进行匹配,一般是通过自身元素的两两交换来满足某个要求,本质上就是自身和自身匹配
#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]]++;for(int i=0;i<(int)s.size();i++){if(s[i]=='1'){if(mp['0']) mp['0']--;else{cout<<n-i<<endl;return;}}else{if(mp['1']) mp['1']--;else{cout<<n-i<<endl;return;}}}cout<<0<<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.C. Game with Multiset
add x表示在集合中添加一个2的x次方
get w问是否可以从集合中取得一些数,和为w
trick:
任何一个数都可以由若干个2的次幂组成有两种方法:
1.将该数转化成二进制,然后一位一位看,如果第x位为1的话,那么 就有 2 x 2^x 2x,具体操作方法可以参考快速幂
int cnt=0; while(x){ if(x&1) ans.push_back(cnt); x>>=1; cnt++; }
2.从高到低贪心
for(int i=29;i>=0;i--){ if(x>=(1ll<<i)){ans.push_back(i);x-=(1ll<<i); } }
但是你会发现,每一位二进制数都只用一次,该题中每个2的次幂不止一个,实际上从大到小贪心和每个2的次幂只用一次的贪心本质上是一样的
如果某个2的次幂有多个的话,那么就只保留一个(有奇数个)或者 一个也不保留(有偶数个),然后其余的进行合成,往上进位
比如说有5个 2 3 2^3 23,想当于1个 2 5 2^5 25
再比如说6个 2 3 2^3 23,相当于1个 2 5 2^5 25以及1个 2 4 2^4 24
所以总能转化成每个2的次幂只用一次
所以从高到低进行贪心,如果能构成就能,不能就不能
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int m;
void solve() {cin>>m;map<int,int>mp;while(m--){int op,x;cin>>op>>x;if(op==1) mp[x]++;else{for(int i=29;i>=0;i--){if(mp[i]){x-=min(x/(1ll<<i),mp[i])*(1ll<<i);}}if(x==0) cout<<"YES"<<endl;else 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;
}
19.D. Ice Cream Balls
n表示想要制作多少种类型的冰淇淋(exactly)
两个球做成一个冰淇淋
问最少需要买几个球(答案总是存在)
之前做过了
二分求出C(x,2)小于等于n,x为不同类型球的数量,然后剩下的补足买之前已经买过的
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
int C2(int x){return x*(x-1)/2;
}
void solve() {cin>>n;int l=1,r=1e10;while(l<r){int mid=(l+r+1)/2;if(C2(mid)>n) r=mid-1;else l=mid;}if(C2(l)==n) cout<<l<<endl;else cout<<l+n-C2(l)<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
20.A. Knapsack
共n个物品,权重为wi
背包容量为W
其实审题这一步很关键,最有用的办法就是一边读题一边用自己的话记录,一边理解一边想
将物品放入背包,使得重量大于等于背包容量的一半,输出一种方案,无解输出-1
发现贪心可行
从大往小贪,只要可以放就放,一旦超过了总容量的一半就可以结束了,如果直到最后都没有超过总容量的一半,就无解
证明:从大到小遍历,首先如果第一个就超过总容量的一半,那么直接结束,否则,就看下一个,由于大的都没有超过总容量的一半,再加一个小的一定不会超过总容量,假设加起来超过总容量的一半,那么直接结束,否则同理,再往下加也不会超过总容量的
trick:
1.其实审题这一步很关键,最有用的办法就是一边读题一边用自己的话记录,一边理解一边想
2.当脑子里立马蹦出个思路,特别是贪心,一定要造不同情况的复杂的极端的样例进行验证
3.如果排序后仍需要知道下标,可以将pair类型存入vector,这比结构体方便
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
int n,w;
void solve() {cin>>n>>w;vector<PII>ans;for(int i=1;i<=n;i++){int x;cin>>x;ans.push_back({x,i});}sort(ans.begin(),ans.end());reverse(ans.begin(),ans.end());int sum=0;vector<int>res;for(int i=0;i<(int)ans.size();i++){if(w>=ans[i].first){sum+=ans[i].first;res.push_back(ans[i].second);if(sum>=(w+1)/2){cout<<res.size()<<endl;for(auto v:res) cout<<v<<' ';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;
}
21.C. Parity Shuffle Sorting
长度为n的数组a(数[0,1e9])
操作:选择两个数,如果两个数的和为奇数,那么让后一个数等于前一个数,如果和为偶数,那么让前一个数等于后一个数
最多n次–>尽可能都用完
使序列非降序,一定有解–>万能通用的方法
操作的对象为两个,可以让一个定死,也可以让两个都定死
有一个不变的点就是里面的数不能凭空出现里面没出现过的数
如果第一个数是偶数的话,那么先让所有偶数都变相同,然后所有的奇数都变得和第一个数一样
如果第一个数是奇数的话,同理
trick:
1.操作的对象为两个,可以让一个定死,也可以让两个都定死
2.有关数学(奇偶数,质数,合数,因数,只要是关于数的)以及构造的,都往奇偶分位以及奇数偶数方向分类想
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];vector<PII>ans;if(a[1]%2==0){int idx=-1;for(int i=n;i>=1;i--){if(a[i]%2==0){idx=i;break;}}if(idx!=1){for(int i=1;i<=n;i++){if(i==idx) break;if(a[i]%2==0) ans.push_back({i,idx});}}for(int i=2;i<=n;i++){if(a[i]%2) ans.push_back({1,i});}}else{int idx=-1;for(int i=n;i>=1;i--){if(a[i]%2){idx=i;break;}}if(idx!=1){for(int i=1;i<=n;i++){if(i==idx) break;if(a[i]%2) ans.push_back({i,idx});}}for(int i=2;i<=n;i++){if(a[i]%2==0) ans.push_back({1,i});}}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;
}
22.A1. Make Nonzero Sum (easy version)
长度为n的数组a(均为正负1)
分段,每个分段的计数方法为加减交替,第一个数为加
满足所有分段加起来为0
如果n为奇数,无解
n为偶数,一定有解,前后两个要么一样,要么不一样,如果一样的话,就放在一段,如果不一样,就分别一段
#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];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(n%2==1){cout<<-1<<endl;return;}vector<PII>ans;for(int i=1;i<=n;i+=2){if(a[i]==a[i+1]) ans.push_back({i,i+1});else ans.push_back({i,i}),ans.push_back({i+1,i+1});}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;
}
23.C. Elemental Decompress
长度为n的数组a(数[1,2e5])
构造两个全排列,使得max(pi,qi)=ai,可能无解
ai并不是全排列
ai确定之后,那么pi和qi都必须小于等于ai,然后其中一个等于ai,另一个小于等于ai
其中一个等于ai后,如果后缀中没有ai了,那么另一个也可以等于ai
首先,将p中还没放置的数都放在set1中,将q中没放置的数都放在set2中,如果当前需要的数不在set1中,那么就需要q来放,如果当前的数在后面没有出现过,那么就p和q放一样的数
最后没有放的位置进行补数,当然得放比ai小的数,可以利用set自带的二分,最后检验
trick:
利用set的二分时,返回迭代器,进行迭代器的加减时特别注意是否越界
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int p[N],q[N];
int n;
void solve() {cin>>n;memset(p,0,sizeof p);memset(q,0,sizeof q);map<int,int>mp,mmp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;set<int>s1,s2;for(int i=1;i<=n;i++) s1.insert(i),s2.insert(i);for(int i=1;i<=n;i++){mmp[a[i]]++;if(s1.count(a[i])){p[i]=a[i];s1.erase(a[i]);if(mmp[a[i]]==mp[a[i]]&&s2.count(a[i])) q[i]=a[i],s2.erase(a[i]);}else if(s2.count(a[i])){q[i]=a[i];s2.erase(a[i]);}}for(int i=1;i<=n;i++){if(p[i]) continue;auto it=s1.lower_bound(a[i]);if(it==s1.begin()){cout<<"No"<<endl;return;}it--;p[i]=*it;s1.erase(it);}for(int i=1;i<=n;i++){if(q[i]) continue;auto it=s2.lower_bound(a[i]);if(it==s2.begin()){cout<<"No"<<endl;return;}it--;q[i]=*it;s2.erase(it);}for(int i=1;i<=n;i++){if(max(p[i],q[i])!=a[i]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;for(int i=1;i<=n;i++) cout<<p[i]<<' ';cout<<endl;for(int i=1;i<=n;i++) cout<<q[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;
}
24.D. Twist the Permutation
长度为n的排列,n在[2,2e3]
操作:n次操作,第i次操作将前i个数循环右移任意次数
问最后能否得到数组a,无解输出-1
输出位移总数最小的方案数
已知最终的结果,要求输出中间的过程->逆推
由于最终结果已知,第n个数是最后一次操作移动的,那么可以得到最后一次操作的最小循环右移次数,并推得最后一次操作前的序列,以此类推
trick:
1.已知最终的结果,要求输出中间的过程->逆推
2.用map预先记录每个数的位置,已知循环右移后的序列,可以快速得到循环右移的次数
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e3+10;
int a[N];
int ans[N];
int n;
void solve(){cin>>n;map<int,int>mp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=i;for(int i=n;i>=1;i--){int res=mp[i]%i;//循环右移的次数ans[i-1]=res;//将1到i循环左移res回到上一状态,相当于循环右移i-resfor(int j=1;j<=i;j++){mp[j]=(mp[j]+i-res)%i;}}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;
}
25.D. Twist the Permutation
n * m的表格 (n和m均小于等于100)
均为0和1
数据小,考虑暴力
进行着色,0表示白色,1表示黑色
一开始全是0,即全是白色
操作:选择子矩形,将其变成国际象棋的颜色(左上角为白色)
输出着色方案,无解输出-1
最左上角的格子必须是0,否则直接-1
从后往前操作,用1 * 2的矩阵去涂色
坑点:
想到从后往前用1 * 2的矩阵,但是没想到可以用竖着的1 * 2矩阵
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=110;
char s[N][N];
int n,m;
struct node{int x1,y1,x2,y2;
};
void solve() {cin>>n>>m;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>s[i][j];}}if(s[1][1]=='1'){cout<<-1<<endl;return;}vector<node>ans;for(int i=n;i>=1;i--){for(int j=m;j>=1;j--){if(s[i][j]=='1'&&j>=2) ans.push_back({i,j-1,i,j});else if(s[i][j]=='1'&&j==1) ans.push_back({i-1,j,i,j});}}cout<<ans.size()<<endl;for(auto v:ans) cout<<v.x1<<' '<<v.y1<<' '<<v.x2<<' '<<v.y2<<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.C. Divisor Chain
目标:将x减为1
可以减x的因子,但是同一个数最多只能减两次
最多需要减1000次,一定有解
由于任何一个数都都可以由若干个2的幂次构成
转化成2进制
先减到2的幂次,然后就好办了
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int x;
void solve() {cin>>x;string s;int a=x;int b=x;while(a){s=(char)((a&1)+'0')+s;a>>=1;}vector<int>ans;int len=s.size();//将x变为2的幂次for(int i=len-1;i>=1;i--){if(s[i]=='1') {ans.push_back(1ll<<(len-1-i));x-=(1ll<<(len-1-i));}}while(x!=1){ans.push_back(x/2);x/=2;}cout<<ans.size()+1<<endl;cout<<b<<' ';for(auto v:ans) cout<<b-v<<' ',b-=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;
}
27.C. Permutation Operations
长度为n的排列
操作:共n次操作,第i次操作选择一个非空后缀,将它们均加i(可以对同一后缀操作很多次)
使得逆序对数量最少
从大到小,整个后缀同时加应该是最优的,因为一下子就增加这么多的正序对
但是交上去错了,这样并不是最优的
发现我们完全可以让逆序对的数量为0
比如说5 3 2 4 1
我们可以让[2,5]都加5,即在第5次操作[2,5],在第3次操作[3,5],在第2次操作[4,5],在第4次操作[5,5]
比较巧妙,当作案例
trick:
两个相邻的数(均为正),要想让小的数大于大的数,让小数加上大的数
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int ans[N];
int n;
void solve() {cin>>n;memset(ans,0,sizeof ans);for(int i=1;i<=n;i++) cin>>a[i];for(int i=2;i<=n;i++) ans[a[i-1]]=i;for(int i=1;i<=n;i++){if(ans[i]) cout<<ans[i]<<' ';else 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;
}
28.C. Ehab and a Special Coloring Problem
构造一个序列,使得从下标2到n中,如果两个下标互质,那么两数不相等
使得序列的最大值最小,无解输出-1
相差为1的两个数互质
两个质数互质
所有质数都互质
所以全部偶数都放1,然后如果是质数的话,那么就依次放不同的数
如果是合数的话,那么就找它的因数,然后和它的因数放一样的答案
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int n;
int ans[N];
bool st[N];
int prime[N];
int cnt;
//欧拉筛
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;get_prime(n);ans[2]=1;int idx=1;for(int i=3;i<=n;i++){if(!st[i]) ans[i]=++idx;else{for(int j=2;j<=i/j;j++){if(i%j==0){ans[i]=ans[j];break;}}}}for(int i=2;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;
}
29.B. Gardener and the Array
长度为n的数组c
问是否有两个不同的子序列,f(a)= f(b),f是将所有数或
只要索引不完全一样,子序列就不同
或:有1出1
如果某个数该位上就它为1,那么称这个数是必要的
如果所有数都是必要的,那么不可能有两个不同的子序列,f(a)= f(b),f是将所有数或
否则可以令a为整个序列,b为在a中剔除一个非必要的
trick:
1.如果想看某位是不是就一个数为1,可以用map桶计数,来记录有多少个数在该位上为12.位运算一般的技巧是将所有数都位运算
3.或是有1出1,只要数a某位上已经为1了,那么只要或上了a,那么该位一定为1,如果b该位也为1,其它位为0,那么或上b结果不变,就称b是非必要的
如果某位就数c为1的话,那么就称c是必要的
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;vector<vector<int>>e(n+1);map<int,int>mp;for(int i=0;i<n;i++){int k;cin>>k;for(int j=0;j<k;j++){int x;cin>>x;e[i].push_back(x);mp[x]++;}}int cnt=0;for(int i=0;i<n;i++){for(int j=0;j<(int)e[i].size();j++){if(mp[e[i][j]]==1){cnt++;break;}}}if(cnt==n) cout<<"No"<<endl;else 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;
}
30.C. Ice and Fire
依次枚举,如果当前为0的话,然后看已经几个连续的0就需要连续靠0赢几次,同理,看几个连续的1就靠1赢几次
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int n;
int ans[N];
string s;
void solve() {cin>>n;cin>>s;s=' '+s;int cnt_0=0,cnt_1=0;for(int i=1;i<=n-1;i++){if(s[i]=='0'){//当前为0,看有几个连续的0cnt_0++;ans[i+1]=i+1-cnt_0;cnt_1=0;} else {cnt_1++;ans[i+1]=i+1-cnt_1;cnt_0=0;}}for(int i=2;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;
}
31.A. Qingshan Loves Strings 2
长度为n的01串
数据比较小,考虑暴力
好字符串:所有对称的字符均不相等
操作:在任意位置插入01
无解输出-1,有解肯定在300次操作以内
暴力模拟
trick:
string的insert函数:s.insert(pos,tmp) 将tmp字符串插入在下标为pos的前面
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;vector<int>ans;int cnt=0;//操作了几次//对称的下标相加为s.size()-1for(int i=0;i<(int)s.size();i++){if(s[i]==s[(int)s.size()-1-i]){if(i==0){string tmp="01";if(s[0]=='1') s=tmp+s,ans.push_back(0),i=0;else ans.push_back((int)s.size()),s+=tmp;}else{if(s[i]=='0'){int pos=(int)s.size()-1-i;//在pos后面插入01string tmp="01";s.insert(pos+1,tmp);ans.push_back(pos+1);}else{int pos=i;//在pos前面插入01string tmp="01";s.insert(pos,tmp);ans.push_back(pos);i-=2;}}cnt++;if(cnt>300) break;}}for(int i=0,j=(int)s.size()-1;i<=j;i++,j--){if(s[i]==s[j]){cout<<-1<<endl;return;} }cout<<ans.size()<<endl;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;
}
32.B. New Year’s Eve
1到n共n个数
从中最多选取k个数,使得选取的所有数异或和最大
最大的数为n,假设n转化成二进制一共cnt位,异或是按位异或,不可能可以进位,所以最多也就cnt位,那么最大也就是cnt位全都为1
那么只要选两个数,一个数为n,另一个数只要把n为0的位补成1就行了
trick:
位运算是按位的,对于参与位运算的若干数,首先最大的那个数转化成二进制后最左边那位肯定是1(忽略前导0),假设共cnt位,按位运算是不可能进位的,所以这些数进行位运算最大也就是cnt位全部为1
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,k;
void solve() {cin>>n>>k;int ans=0;int num=1;if(k==1){cout<<n<<endl;return;}while(n){ans^=num;num<<=1;n>>=1;}cout<<ans<<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.C. Insert Zero and Invert Prefix
长度为n的01序列
操作:n次操作,第i次操作在0到i-1之间选择一个整数p,在序列b的p位置后插入0,并反转它前面所有元素
最后一个必须是0,否则无解
从后往前,数连续1的个数cnt,可以先产生cnt个0,然后将cnt个0反转为1
trick:
1.操作是插入一个0并将前缀反转,并不会影响后面的序列,考虑从后往前
2.01反转,考虑一段一段,连续cnt个1可以将连续cnt个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;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(a[n]==1){cout<<"NO"<<endl;return;}cout<<"YES"<<endl;int cnt=0;for(int i=n-1;i>=0;i--){if(a[i]==1){cnt++;cout<<0<<' ';}else{cout<<cnt<<' ';cnt=0;}}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;
}
34.B. Binary String Constructing
构造长度为a+b的01串,刚好a个0,b个1
刚好有x对相邻对不相等
一定有解
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int a,b,x;
void solve() {cin>>a>>b>>x;if(a<=b){//1比较多if(x%2==1){for(int i=0;i<x/2;i++) cout<<"10";for(int i=0;i<b-x/2;i++) cout<<1;for(int i=0;i<a-x/2;i++) cout<<0;cout<<endl;}else{for(int i=0;i<x/2;i++) cout<<"10";for(int i=0;i<a-x/2;i++) cout<<0;for(int i=0;i<b-x/2;i++) cout<<1;cout<<endl;}}else{if(x%2==1){for(int i=0;i<x/2;i++) cout<<"01";for(int i=0;i<a-x/2;i++) cout<<0;for(int i=0;i<b-x/2;i++) cout<<1;cout<<endl;}else{for(int i=0;i<x/2;i++) cout<<"01";for(int i=0;i<b-x/2;i++) cout<<1;for(int i=0;i<a-x/2;i++) cout<<0;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;
}
35.A. Packets
n个硬币
将n枚硬币分成若干组,使得1到n这些数都可以由某几组构成
求最少分成几组
要使组数最少,那么每组硬币数可以跨度大一些,想到2的幂次
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;int ans=0;for(int i=0;;i++){int x=(1ll<<i);if(n>=x){ans++;n-=x;}else break;}if(n) ans++;cout<<ans<<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.C. Insert and Equalize
长度为n的数组a(数[-1e9,1e9])每个数均不同
操作:
首先在末尾加入一个没出现过的数,选择一个正整数x
然后每次操作将x加到其中一个元素上
目标是让所有元素相等,问最少操作几次
先排个序
只能变大,不能变小,那么就都变成最大的那个
要使操作次数少,那么每次加的数要大一些,所有其它数和最大值的差值的gcd即为x,得到x之后就很简单了
那么有没有可能a[n+1]作为最大的数,然后其它n个数和a[n+1]的差值全部取gcd会更大呢?
不可能!!!
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;
int gcd(int a,int b){if(b==0) return a;return gcd(b,a%b);
}
void solve() {cin>>n;map<int,int>mp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;if(n==1){cout<<1<<endl;return;}sort(a+1,a+1+n);int x=0;for(int i=1;i<=n-1;i++){x=gcd(x,a[n]-a[i]);}int res=a[n];while(mp[res]){res-=x;}int ans=0;for(int i=1;i<=n;i++){ans+=(a[n]-a[i])/x;}ans+=(a[n]-res)/x;cout<<ans<<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. Vasya and Isolated Vertices
由n个顶点,m条边组成的无向图
求孤立顶点的最少和最多数量
最多:x个顶点连成完全无向图,所用边数大于等于m,然后剩下节点成为孤立节点
最少:两两顶点一对,用一条边,n-m*2
trick:
图论一定要有特判,n为1和m为0
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
int cal(int x){//x个顶点return x*(x-1)/2;//成为完全无向图需要的边数
}
void solve() {cin>>n>>m;if(m==0){cout<<n<<' '<<n<<endl;return;}int l=1,r=n;while(l<r){int mid=(l+r)/2;if(cal(mid)>=m) r=mid;else l=mid+1;}cout<<max(0ll,n-m*2)<<' '<<max(0ll,n-l)<<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. Fill in the Matrix
n*m的矩阵
每一行要求是1到m的排列
vi是第i列mex值
beauty:v1到vm的mex
要求使mex最大
构造矩阵
0 1 2 3
1 2 3 0
2 3 0 1
先构造m-1行
再0 1 2 3重复
答案为m+1
0 1 2 3
1 2 3 0
不够m-1行,答案为n+1
0 1 2 3 4
1 2 3 4 0
特判m为1的情况
trick:
1.构造矩阵,一边遍历一边输出,很少存到数组再输出的
2.构造矩阵,一般要特判n为1,m为1
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;if(m==1){cout<<0<<endl;for(int i=0;i<n;i++) cout<<0<<endl;return;}if(n<m-1){cout<<n+1<<endl;for(int i=0;i<n;i++){for(int j=0;j<m;j++){cout<<(j+i)%m<<' ';}cout<<endl;}}else{cout<<m<<endl;for(int i=0;i<m-1;i++){for(int j=0;j<m;j++){cout<<(j+i)%m<<' ';}cout<<endl;}for(int i=0;i<n-(m-1);i++){for(int j=0;j<m;j++){cout<<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;
}
39.B. Bit Flipping
长度为n的01串
操作:选择一位不反转,其余位均反转
一共操作k次,使得字典序最大
01反转看奇数次,偶数次,如果反转奇数次,那么反转,否则不变
尽可能把前面的0 变成1
总反转次数为k,该位的反转次数即为k减去该位上的数字
从前往后遍历,如果是1的话,就需要保持不变,如果k为奇数,那么该位先放个1,使得该位操作次数为偶数 ,如果k为偶数,那么该位不用放数字
如果是0的话,就需要反转,如果k为奇数,那么该位不用放数字,如果k为偶数 ,那么该位放个1,最后如果k还有剩余,那么就全放在最后一位
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int ans[N];
int n,k;
string s;
void solve() {cin>>n>>k;cin>>s;memset(ans,0,sizeof ans);int tmp=k;for(int i=0;i<n;i++){if(s[i]=='1'){if(tmp&&k%2) ans[i]=1,tmp--;else if(tmp&&k%2==0) ans[i]=0;}else{if(tmp&&k%2) ans[i]=0;else if(tmp&&k%2==0) ans[i]=1,tmp--;}if(!tmp) break;}ans[n-1]+=tmp;for(int i=0;i<n;i++){if((k-ans[i])%2){if(s[i]=='1') s[i]='0';else s[i]='1';}}cout<<s<<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.A. Oh Those Palindromes
长度为n的字符串,由小写字母组成
回文计数:回文子串的个数
重新排列字符串,使得回文子串个数最多
aabbccddfgggghhh
16+2+1+1+1+1+6+3
aabb
abba
枚举了好几种情况,发现都是一样的排在一起是最多的
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;multiset<char>s1;for(int i=0;i<n;i++) s1.insert(s[i]);for(auto v:s1) 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;
}
41.C. Salyg1n and the MEX Game
共n个不同的整数在集合S中
Alice和Bob博弈
Alice先行
Alice在集合S中添加一个S中没出现过的正整数x([0,1e9])
Bob在集合S中移除一个数y,y必须严格小于S中的最后一个数
当Bob移除不了数或者Alice走了n+1步,游戏结束,结果为mex(S)
Alice使结果最大化,Bob使结果最小化
Alice先行,先补上mex
然后当Bob移除一个数,Alice就把那个数加回来
trick:
1.交互题,程序中需要输入的就正常输入,需要输出的就在每个输出语句后面加上一句cout.flush();
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;int x;//没有出现的第一个数for(int i=0;i<n;i++){if(!mp[i]){x=-1;break;}}cout<<x<<endl;fflush(stdout);int y;while(cin>>y){if(y<0) break;cout<<y<<endl;fflush(stdout);}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}
42.C. Labs
nn个实验室建在不同的高度,1到nn,高度依次递增
高处的实验室可以向低处的实验室输送水,一次输送一单位的水
将n*n个实验室分成n组,每组包含n个实验室
f(A,B):A组向B组输水,可以输多少单位的水
构造一种分组,使得任意两组的f,所有情况取min,尽可能大
从样例寻求到了一种构造方法:
1到n分别放在每一组中
n+1到2*n分别放到每一组中
以此类推
放的方式是正序,逆序,正序,逆序…
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;int flag=1;vector<vector<int>>e(n+1);int cnt=0;for(int i=1;i<=n;i++){//共n组if(flag){//正序for(int j=1;j<=n;j++){e[j].push_back(++cnt);}}else{for(int j=n;j>=1;j--){e[j].push_back(++cnt);}}flag^=1;}for(int i=1;i<=n;i++){for(auto v:e[i]) 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;
}
43.B. Painting Pebbles
一共n堆鹅卵石
ai为第i堆鹅卵石的数量
构造用k种颜色给每颗鹅卵石上色
使得任意两堆鹅卵石中颜色c的数量最多差1个,要求k种颜色都满足,无解输出NO
平均分配
一遍一遍遍历,只要每组都还有石头可涂,那么每组涂一个(用同一种颜色)
只要有任何一组没有石头可涂,那么这组涂完之后(用同一种颜色),就换颜色
直到所有的石头都被涂上色
如果颜色不够了,就无解
数据比较小,直接暴力
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=110;
int a[N];
int n,k;
void solve() {cin>>n>>k;int color=1;int sum=0;for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];vector<vector<int>>ans(n+1);int cnt=0;//涂了几个石头while(1){bool ok=true;for(int i=1;i<=n;i++){if((int)ans[i].size()<a[i]){//还有石头可涂ans[i].push_back(color);cnt++;}else ok=false;}if(!ok) color++;if(color>k||cnt==sum) break;}if(cnt<sum){cout<<"NO"<<endl;return;}cout<<"YES"<<endl;for(int i=1;i<=n;i++){for(auto v:ans[i]) 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;
}
44.B. Sonya and Exhibition
长度为n的01串
beauty:[l,r]中0的个数和1的个数的乘积
给定m个区间
构造01串,使得m个区间美之和最大
和一定,差越小,积越大
0和1平均分配
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;while(m--){int l,r;cin>>l>>r;}int flag=1;for(int i=0;i<n;i++){if(flag) cout<<1;else cout<<0;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;
}
45.A. Difference Row
长度为n的数组a(数[-1000,1000])n大于等于2
排列,使得相邻两数差(前一个减后一个)之和最大,并且排列的字典序最小
最后的值为x1-xn,所以x1放最大,xn放最小,由于要字典序最小,中间升序
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
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];sort(a+1,a+1+n);reverse(a+1,a+1+n);vector<int>ans;for(int i=2;i<=n-1;i++) ans.push_back(a[i]);sort(ans.begin(),ans.end());cout<<a[1]<<' ';for(int i=0;i<(int)ans.size();i++) cout<<ans[i]<<' ';cout<<a[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;
}
46.B. Neko Performs Cat Furrier Transform
对于正整数x(数[1,1e6])
完美长猫数:转化为二进制数全为1
操作:A.将x和一个完美长猫数异或 B.+1
第奇数次操作A,第偶数次操作B
最多执行40次,一定有解
将x变成完美长猫数
和1异或相当于01反转
从高位到低位一位一位反转
但是要注意,加1可能会进位,导致前面的1变成0,所以每次都从最高位开始找
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int x;
void solve() {cin>>x;int xx=x;int xxx=x;int cnt=-1;//x转化为二进制最高位是第几位while(xx){cnt++;xx>>=1;}vector<int>ans;while(x&(x+1)){for(int i=cnt;i>=0;i--){if((x&(1ll<<i))==0){//如果该位是0的话,就反转为1x^=((1ll<<(i+1))-1);if(x&(x+1)) x++;ans.push_back(i+1);}}}if(xxx%2==0) cout<<ans.size()*2-1<<endl;else cout<<ans.size()*2<<endl;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;
}
47.C. Dividing the numbers
将1到n分到两个集合中,两个集合都不能为空,使得两组整数之和的差值最小
任意四个连续的数,x,x+1,x+2,x+3,可得x+x+3=x+1+x+2
所以每四个连续的数可以抵消,最后看n%4即可
trick:
任意四个连续的数,x,x+1,x+2,x+3,可得x+x+3=x+1+x+2
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin >> n;int x = n % 4;if (x == 1 || x == 2) cout << 1 << endl;else cout << 0 << endl;vector<int>ans;for (int i = n; i >= 4; i -= 4) {ans.push_back(i);ans.push_back(i - 3);}if (x == 3) ans.push_back(3);else if (x == 2) ans.push_back(2);cout << ans.size() << ' ';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;
}
48.A. Lucky Permutation Triple
构造三个长度为n的排列,0到n-1各出现一次
满足任意i,(ai+bi)%n = ci
无解输出-1
当n为奇数时,可以构造a为0到n-1,b为0到n-1
猜测n为偶数时无解
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;if(n%2){for(int i=0;i<n;i++) cout<<i<<' ';cout<<endl;for(int i=0;i<n;i++) cout<<i<<' ';cout<<endl;for(int i=0;i<n;i++) cout<<i*2%n<<' ';cout<<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.B. Students in Railway Carriage
有n个连续的座位,.表示位置是空的,*表示有人
a名学生程序员,b名学生运动员,问最多有几人可以做到座位上,学生程序员不能连续,学生运动员不能连续
交替放置
优先放多的那个
取出一段一段连续的.
#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;if(a<b) swap(a,b);vector<int>ans;//存放大于等于2的int sum=0;//统计等于1的个数int cnt=0;for(int i=0;i<n;i++){if(s[i]=='.') cnt++;else{if(cnt>=2) ans.push_back(cnt);else if(cnt==1) sum++;cnt=0;}}if(cnt>=2) ans.push_back(cnt);else if(cnt==1) sum++;int res=0;for(int i=0;i<(int)ans.size();i++){int flag=1;if(a<b) swap(a,b);//每次优先填大的for(int j=0;j<ans[i];j++){if(flag){if(a){res++;a--;}}else{if(b){res++;b--;}}flag^=1;}}//为1的填什么都行int res1=a+b;//剩下的res+=min(res1,sum);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;
}
50.B. Vika and Squares
一共有n个带颜色的罐子
ai表示颜色i罐子油漆的升数
从左到右给正方形涂颜料,一个正方形需要1升,必选按照x+1的循环顺序,起点颜料自定
问最多可以涂多少个正方形
取所有数的最小值minn,首先minn轮一定是可以的,然后就会出现很多个0造成阻断,那么只要统计连续非0个数最多是多少
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[2*N];
int n;
void solve() {cin>>n;int ans=0;int minn=2e9;for(int i=1;i<=n;i++) cin>>a[i],minn=min(minn,a[i]);for(int i=1;i<=n;i++) a[i]-=minn;for(int i=n+1;i<=2*n;i++) a[i]=a[i-n];ans+=minn*n;int cnt=0;int maxn=0;for(int i=1;i<=2*n;i++){if(a[i]) cnt++;else{maxn=max(maxn,cnt);cnt=0;}}if(cnt) maxn=max(maxn,cnt);cout<<ans+maxn<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
// cin>>t;while(t--) {solve();}return 0;
}