codeforces 1300分

文章目录

  • 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
#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
#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
#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
#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 
#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
#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
#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
#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
#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<
	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 
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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-res
		for(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
#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
#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
#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
#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桶计数,来记录有多少个数在该位上为1

2.位运算一般的技巧是将所有数都位运算

3.或是有1出1,只要数a某位上已经为1了,那么只要或上了a,那么该位一定为1,如果b该位也为1,其它位为0,那么或上b结果不变,就称b是非必要的

如果某位就数c为1的话,那么就称c是必要的

#include
#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
#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,看有几个连续的0
			cnt_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
#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()-1
	for(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后面插入01
					string tmp="01";
					s.insert(pos+1,tmp);
					ans.push_back(pos+1);
				}
				else{
					int pos=i;//在pos前面插入01
					string 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
#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
#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
#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
#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会更大呢?

不可能!!!

codeforces 1300分_第1张图片

trick:

直接理解记忆这张图

#include
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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
#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的话,就反转为1
				x^=((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 
#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
#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
#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
#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;
}

你可能感兴趣的:(codeforces,算法,图论,c++,数据结构)