codeforces 1400分

文章目录

  • 1.[B. Phoenix and Beauty](https://codeforces.com/problemset/problem/1348/B)
  • 2.[C. Rotation Matching](https://codeforces.com/problemset/problem/1365/C)
  • 3.[C. Element Extermination](https://codeforces.com/problemset/problem/1375/C)
  • 4.[D. Epic Transformation](https://codeforces.com/problemset/problem/1506/D)
  • 5.[C. Team](https://codeforces.com/problemset/problem/401/C)
  • 6.[B. Find The Array](https://codeforces.com/problemset/problem/1463/B)
  • 7.[B. Quasi Binary](https://codeforces.com/problemset/problem/538/B)
  • 8.[C. Heavy Intervals](https://codeforces.com/contest/1909/problem/C)
  • 9.[D. Game With Array](https://codeforces.com/problemset/problem/1355/D)
  • 10.[B. Composite Coloring](https://codeforces.com/contest/1332/problem/B)
  • 11.[B. Applejack and Storages](https://codeforces.com/problemset/problem/1393/B)
  • 12.[C. Phoenix and Towers](https://codeforces.com/problemset/problem/1515/C)
  • 13.[A. Meximum Array](https://codeforces.com/problemset/problem/1628/A)
  • 14.[D. Productive Meeting](https://codeforces.com/contest/1579/problem/D)
  • 15.[B2. Wonderful Coloring - 2](https://codeforces.com/problemset/problem/1551/B2)
  • 16.[C. Array Game](https://codeforces.com/contest/1904/problem/C)
  • 17.[C. Sum of Substrings](https://codeforces.com/problemset/problem/1691/C)
  • 18.[D. Diverse Garland](https://codeforces.com/problemset/problem/1108/D)
  • 19.[D. Absolute Sorting](https://codeforces.com/problemset/problem/1772/D)
  • 20.[C. LIS or Reverse LIS?](https://codeforces.com/problemset/problem/1682/C)
  • 21.[C. Not Assigning](https://codeforces.com/problemset/problem/1627/C)
  • 22.[C. awoo's Favorite Problem](https://codeforces.com/problemset/problem/1697/C)
  • 23.[E. Vlad and a Pair of Numbers](https://codeforces.com/problemset/problem/1790/E)
  • 24.[C. Make Good](https://codeforces.com/problemset/problem/1270/C)
  • 25.[B. AND Sequences](https://codeforces.com/problemset/problem/1513/B)
  • 26.[B. Suffix Operations](https://codeforces.com/problemset/problem/1453/B)
  • 27.[D1. Mocha and Diana (Easy Version)](https://codeforces.com/problemset/problem/1559/D1)
  • 28.[C. Fillomino 2](https://codeforces.com/problemset/problem/1517/C)
  • 29.[A1. Dual (Easy Version)](https://mirror.codeforces.com/contest/1854/problem/A1)
  • 30.[C. No Prime Differences](https://codeforces.com/contest/1838/problem/C)
  • 31.[C. Matching Arrays](https://codeforces.com/contest/1896/problem/C)
  • 32.[E. Add Modulo 10](https://codeforces.com/contest/1714/problem/E)
  • 33.[D. Bracket Coloring](https://codeforces.com/problemset/problem/1837/D)
  • 34.[D1. Zero-One (Easy Version)](https://codeforces.com/problemset/problem/1733/D1)
  • 35.[C. Palindromifier](https://codeforces.com/problemset/problem/1421/C)
  • 36.[B. Hossam and Friends](https://codeforces.com/problemset/problem/1771/B)
  • 37.[C. Column Swapping](https://codeforces.com/problemset/problem/1684/C)
  • 38.[A. Bear and Prime 100](https://codeforces.com/problemset/problem/679/A)
  • 39.[D. Row Major](https://codeforces.com/problemset/problem/1844/D)
  • 40.[C. Fishingprince Plays With Array](https://codeforces.com/problemset/problem/1696/C)
  • 41.[B. String Modification](https://codeforces.com/problemset/problem/1316/B)
  • 42.[C. Bricks and Bags](https://codeforces.com/problemset/problem/1740/C)
  • 43.[D. Equalize Them All](https://codeforces.com/problemset/problem/1144/D)
  • 44.[A. Grid game](https://codeforces.com/problemset/problem/1103/A)
  • 45.[B. Pasha and String](https://codeforces.com/problemset/problem/525/B)
  • 46.[C. Theofanis' Nightmare](https://codeforces.com/problemset/problem/1903/C)
  • 47.[C. Diverse Matrix](https://codeforces.com/problemset/problem/1266/C)
  • 48.[C. Complementary XOR](https://codeforces.com/problemset/problem/1750/C)
  • 49.[C. Set Construction](https://codeforces.com/problemset/problem/1761/C)
  • 50.[B. Arrays Sum](https://codeforces.com/problemset/problem/1408/B)

1.B. Phoenix and Beauty

两个整数n和k(有一个坑点是它括号里写了n,k的大小顺序,然后把k放前面,然后就以为先输入k)
长度为n的序列
美丽序列:所有长度为k的连续子序列和相同
插入若干整数(1到n)使得序列变成美丽序列,无解输出-1

数据比较小,直接暴力枚举
题目中说如果存在的话,长度不超过1e4,那么肯定是通过暴力不断插入

有一种通用的万能的构造方法,就是一直在每组k的后面插入,然后维护前面的全满足

比如 1 2 3 4 ,k=3
那么要满足第二组k个,那么就要在3后面插个1,同理,依次需要按顺序插入2,3,4然后如果要插入的数就是待插的数那么很好,如果没有就要自己插入新的数,直到最后一个待插入的数插进去,题目中说了如果存在的话,那么长度不超过1e4,那么当长度超过1e4时若还没有插完,那么无解
事实证明这种构造方法并不对,因为如果待插入的数在前面没出现过的话,那么永远也插不进去

可以发现,如果k为4的话,那么必须刚好4种数字,然后一直循环,这样才能满足题意,如果数字种数超了k个,那么无解,否则可以通过补数和循环,循环次数的话可以用10000/数字种数,因为题目数据保证了长度最大10000,所以可以直接让长度最大,那么肯定保证循环次数足够多

坑点:括号里写了n,k的大小顺序,然后把k放前面,然后就以为先输入k

trick:

1.自以为想出了一种万能的通用的构造方法,实际是错的,避免方法是先通过样例检验,然后自己再造一些极端的数据来验证

2.如果一个数组中所有长度为 k的子数组的和都相同,那么这个数组就是美丽的。数组的子数组是任何连续元素的序列----这是美丽数组的性质,我们可以通过手玩举一些例子来挖掘其背后的性质:数字种数必须小于等于k,然后不断循环,这样才能满足性质,这一性质的挖掘是这题解题的关键

3.题目说了构造长度不超过10000,那么我们不用考虑多长,就直接长度最大化,接近10000,这样就不用考虑要多长了

4.数据比较小,暴力是一个很好的方向

#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;set<int>s;map<int,int>mp;for(int i=1;i<=n;i++) cin>>a[i],s.insert(a[i]),mp[a[i]]++;int maxn=0;for(auto v:mp){maxn=max(maxn,v.second);}if((int)s.size()>k){cout<<-1<<endl;return;}int len=s.size();vector<int>ans;while(s.size()){ans.push_back(*s.begin());s.erase(s.begin());}for(int i=0;i<k-len;i++) ans.push_back(1);cout<<10000/(int)ans.size()*(int)ans.size()<<endl;for(int i=1;i<=10000/(int)ans.size();i++){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;
}

2.C. Rotation Matching

长度为n的a序列和b序列是全排列
操作:将序列循环右移或左移
操作不限次数

求最大匹配元素对数量
匹配:大小和位置均相同

只要移动一个序列即可

最—贪心,一边遍历,一边贪,不可行
猜一下:让第一个数匹配,然后数匹配总数,不可行,因为可能虽然第一个不匹配,但是其它匹配了很多
挖掘操作背后的性质,什么是一定的,什么是不变的—想不到,感觉就算想挖掘也想不出,这作为其中一个思考的方向,当然想不出也没办法,也很正常,因为一个题目在被出出来的时候,基本解题方向已经被限死了,很小范围,所以这个方向想不出是很正常的,作为一个可能的方向,想不出就换方向

统计每个b i需要往右移多少位和a j 对齐(b i ==a j )然 后 看 看 往 右 移 动 几 次 对 答 案 最 多

trick:

1.反向思考,先按照正常的朴素的正向思维想(往往不饶弯子),比如这题就是每循环右移一次(0到n次),求出匹配的数量,然后取最大匹配数量即可,但是这样肯定超时了,所以反过来思考(转一次弯),将每个字符匹配累加到循环右移的次数上,这样利用一个map,就不会超时了,遍历b的每个数,将每个数移到正确位置的次数++,比如将3移到正确的位置需要两次,将4移到正确的位置也需要两次,那么移动两次匹配数量则为2

2.挖掘操作背后的性质,什么是一定的,什么是不变的—想不到,感觉就算想挖掘也想不出,这作为其中一个思考的方向,当然想不出也没办法,也很正常,因为一个题目在被出出来的时候,基本解题方向已经被限死了,很小范围,所以这个方向想不出是很正常的,作为一个可能的方向,想不出就换方向

#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;map<int,int>mp,pos;for(int i=1;i<=n;i++) cin>>a[i],pos[a[i]]=i;for(int i=1;i<=n;i++) cin>>b[i];for(int i=1;i<=n;i++){if(pos[b[i]]>=i) mp[pos[b[i]]-i]++;else{mp[pos[b[i]]+n-i]++;}}int ans=0;for(int i=0;i<=n;i++){ans=max(ans,mp[i]);}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;
}

3.C. Element Extermination

长度为n的数组a,最初为全排列
操作:可以移除相邻的正序的两个数的其中一个
次数不限
问能否让数组长度变成1

完全升序的肯定可以
降升也可以,两降就不行

逆序对的数量(包括相等的对数)如果大于等于2,那么就NO,否则YES
坑点:有的题目YES,NO大小写不区分,有的题目区分大小写
逆序对用归并排序
逆序对错了,想出一个思路一定得用样例仔细验证,不要看一眼感觉对,代码都写出来了才发现思路错了
不要通过样例来猜做法,首先样例并不全,其次样例具有误导性,还是应该通过题目给的信息来自己手玩造样例来推测(要造那种复杂的,然后不同种类的多一些,全一些),题目所给样例只是用来验证思路的正确性的

通过手玩造复杂样例可以发现,只要第一个元素小于最后一个元素,那么YES,否则NO

坑点:

1.有的题目YES,NO大小写不区分,有的题目区分大小写

2.想出一个思路一定得用样例仔细验证,不要看一眼感觉对,代码都写出来了才发现思路错了、

trick:

手玩造复杂,不同种类的样例

不要通过样例来猜做法,首先样例并不全,其次样例具有误导性,还是应该通过题目给的信息来自己手玩造样例来推测(要造那种复杂的,然后不同种类的多一些,全一些),题目所给样例只是用来验证思路的正确性的

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=3e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];if(a[1]<a[n]) 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;
}

4.D. Epic Transformation

长度为n的数组a
操作:删除两个不同的元素
输出数组最少剩下几个元素

与顺序无关 ,先排个序
成对删除,如果是奇数个,至少剩一个
先把数量最多的消耗,不然它自己是消耗不了的
通过手玩发现,如果最高的那个数量大于等于其它所有之和,那么答案即为最高的减去其它之和
否则,一定可以消完,当然偶数个消成0,奇数个消成1

trick:手玩

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;map<int,int>mp;int sum=0;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;int maxn=0;for(auto v:mp){maxn=max(maxn,v.second);sum+=v.second;}if(maxn>=sum-maxn) cout<<maxn-(sum-maxn)<<endl;else{if(n%2==1) cout<<1<<endl;else 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;
}

5.C. Team

一共有n张数字0,m张数字1
构造序列,使得不能有两张0相邻,不能有连续3个1

一个0配一个1或者两个1

一个0最多配两个1,然后最多最后再来两个1
一个0最少配一个1

trick:这边主要用到平均分的技巧(上次已经总结过了)

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;if(n>m+1||2*n+2<m){cout<<-1<<endl;return;}//特判if(2*n+2==m){cout<<"11";for(int i=0;i<n;i++) cout<<"011";cout<<endl;return;}if(2*n+1==m){cout<<"1";for(int i=0;i<n;i++) cout<<"011";cout<<endl;return;}if(n==m+1){cout<<"0";for(int i=0;i<n-1;i++) cout<<"10";cout<<endl;return;}//平均分int res=m-n;//一个0配一个1之后还剩几个1for(int i=0;i<res;i++) cout<<"011";for(int i=0;i<n-res;i++) cout<<"01";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;
}

6.B. Find The Array

长度为n的数组a
beautiful:任意相邻两个数,其中一个数是另一个数的因数,ai和bi的差的绝对值的和的两倍小于等于S
构造美丽数组(答案总是存在)–>万能通用的构造方法

与顺序无关,排个序
b1先等于a1,然后依次遍历,如果ai-1是bi-1的倍数,那么就可以,否则,bi就延续bi-1,但其实并不科学,不能证明,甚至自己觉得可以构造出反例

让数组b的奇数位都等于1,偶数位和数组a的偶数位对应相等或者让数组b的偶数位都等于1,奇数位和数组a的奇数位对应相等

这样的话,奇数位相差都为0或者偶数位相差都为0,其中有一种情况能满足差的绝对值的和的两倍小于等于S,只要分别构造出来,检验即可

trick:

1.两倍可以往奇偶位的方向想

2.相邻两个数满足一个数能被另一个数整除,比较特殊万能的就是1,因为1能够整除任意一个数

3.如果有一个思路,那么需要去验证正确性,如果代码比较容易写的话, 那么直接写代码验证,如果代码不容易写,那么就通过构造例子,看能否举出反例,这是策略

4.如果构造序列需要判断一些条件,比较麻烦,可以直接将可能的序列构造出来 ,检验即可

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=55;
int a[N],b[N];
int n;
void solve() {cin>>n;int sum=0;for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i];for(int i=1;i<=n;i++){if(i%2==1) b[i]=1;else b[i]=a[i];}int res=0;for(int i=1;i<=n;i++){res+=abs(a[i]-b[i]);}if(res*2<=sum){for(int i=1;i<=n;i++) cout<<b[i]<<' ';cout<<endl;return;}for(int i=1;i<=n;i++){if(i%2==1) b[i]=a[i];else b[i]=1;}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;
}

7.B. Quasi Binary

将正整数n表示成准二进制数之和
要求准二进制数的个数最少
准二进制数:只包含0和1的十进制数

因为最小可以为1(0用不着),所以所有数都是可得的
两位数的话,最大的准二进制数可以为11,最小为10
三位数的话,最小的准二进制数为100,最大的准二进制数为111

贪心

从大的开始贪

从高位到低位,该位为x,如果x小于等于1,那么就等于x,如果x大于1,那么后面所有位都变成1

这样贪是错的,因为这样可能使得1的个数过多

我们一位一位看,对于某一位来说,只有可能是0和1,然后我们就分别凑每一位就行了,比如说某一位是x,那么该位就需要x个1,最少个数即所有位上的数的最大值

那么如何构造呢?例如523,从最低位开始遍历,将3平均分到前3个数中,将20平均分到前2个数中,将500平均分到前5个数中

trick:

1.如果思考方向为贪心的话,尤其要验证思路,通过造极端复杂的样例,看思路是否正确

2.对于凑十进制数(只能存在1和0,主要是1),一个思考方向是拆位,分别凑每一位,方法是每一位上的数x平均放到前x个数中(要乘以幂)

#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;string s=to_string(n);int len=s.size();int cnt=0;for(int i=0;i<len;i++) cnt=max(cnt,(int)(s[i]-'0'));for(int i=len-1,mi=0;i>=0;mi++,i--){int digit=s[i]-'0';for(int j=0;j<digit;j++){a[j]+=pow(10,mi);}}cout<<cnt<<endl;for(int i=0;i<cnt;i++) cout<<a[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

8.C. Heavy Intervals

一共有n个区间,第i个区间的单位长度权重为ci
操作:重新排列l,r,c
求所有区间权重之和最小是多少

可以得到所有区间长度和是一定的,然后n个区间的单位长度权重也是一定的,所以就是对一整段长度一定的区间进行分割,给予其不同的单位权重,使得总权重最小,那么就要让平均权重最小,将更小的单位权重给更长的区间,这样可以让平均权值最小

所以就是每次选取当前最短的区间,做法是先将l升序,将r放在set中,对于最大的l,在set中进行二分找到大于它的第一个,构成的区间即为最短的区间,然后在set中删除该元素

区间长度升序,然后权重降序,相乘并相加

trick:
1.找到什么是一定的,这个很重要,往往是解题的关键,比如区间总长度一定,要使得总权重最小,那么就要让平均权重最小

2.在set中进行二分,s.upper_bound(x)表示set中大于x的第一个元素,返回其迭代器

3.set删除元素

erase(iterator) ,删除定位器iterator指向的值

erase(key_value),删除键值key_value的值

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int l[N];
int c[N];
int n;
void solve() {cin>>n;for(int i=0;i<n;i++) cin>>l[i];set<int>s;for(int i=0;i<n;i++) {int x;cin>>x;s.insert(x);}for(int i=0;i<n;i++) cin>>c[i];sort(l,l+n);sort(c,c+n);reverse(c,c+n);vector<int>ans;for(int i=n-1;i>=0;i--){auto it=s.upper_bound(l[i]);//在set中找到大于l[i]的第一个元素,返回其迭代器int r=*it;ans.push_back(r-l[i]);s.erase(it);}sort(ans.begin(),ans.end());int res=0;for(int i=0;i<n;i++){res+=c[i]*ans[i];}cout<<res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

9.D. Game With Array

P构造和为S的N个正整数,选择一个在[0,S]的一个元素k
V在以上数组中选择若干元素之和等于K才能赢
问P能否获胜

坑点:YES,NO

正整数,最小为1
手玩
N为1肯定YES
N小于等于S则YES,构造方法即为平均分,1是没有出现的
2 3
1 2

2 4
2 2

3 4
1 1 2

3 5
1 2 2

3 6

2 2 2

trick:

1.手玩找出的规律

2.平均分这一技巧是之前积累过的,为这题构造提供了一个好方法

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,s;
void solve() {cin>>n>>s;if(n==1){if(s==1) cout<<"NO"<<endl;else{cout<<"YES"<<endl;cout<<s<<endl;cout<<s-1<<endl;}return;}if(2*n<=s){cout<<"YES"<<endl;int ave=s/n;//平均分int res=s-ave*n;//剩下的vector<int>ans;for(int i=0;i<n;i++) ans.push_back(ave);for(int i=0;i<res;i++) ans[i]++;for(auto v:ans) cout<<v<<' ';cout<<endl;cout<<1<<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;
}

10.B. Composite Coloring

长度为n的数组a,均为合数
选择小于等于11的一个整数m
1到m共m中颜色,给所有元素进行着色
要求:1到m都要被用到,所有元素都要有颜色,颜色相同的两个元素不能互质
题目一定有解

数据比较小,可以考虑暴力,直接对于每种颜色,去遍历其它数,如果不互质,那么就涂相同的颜色,这样不行,因为相同颜色的必须全部都不互质
如果n小于等于11的话,那么直接n种颜色全不同即可
可以简化一下,全部偶数都取同一种颜色,然后只要看奇数,通过手玩,由于均为合数,所以数比较少,然后由于合数可以分解成若干个质数的乘积,发现从3开始,5,7,11,13,17,19,23,29,31,一直到小于1000,用个质数筛就行了

坑点:读题还是看英文,当看不懂的时候再看一眼翻译,因为有些词翻译并不准确

trick:

1.合数可以分解成若干个质数的乘积(唯一),之前积累过的,在这里又用到了

2.分奇偶是一个很好的方向,如何去想呢?只要是有关数学的,比如因数,质数,合数

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1010;
int a[N];
int color[N];
bool st[N];
int prime[N];
int 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;memset(color,0,sizeof color);for(int i=1;i<=n;i++) cin>>a[i];int ans=0;map<int,int>mp;for(int i=1;i<=n;i++){for(int j=0;j<cnt;j++){if(a[i]%prime[j]==0){
//				cout<<i<<' '<<prime[j]<<endl;if(mp[prime[j]]==0){ans++;color[i]=ans;mp[prime[j]]=ans;}else color[i]=mp[prime[j]];break;}}}cout<<ans<<endl;for(int i=1;i<=n;i++){cout<<color[i]<<' ';}cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;get_prime(1000);cin>>t;while(t--) {solve();}return 0;
}

11.B. Applejack and Storages

初始共n个木板,长度为ai
共q个订单,+x代表收到长度为x的木板,-x代表取出长度为x的木板

问每次订单之后能否建造两个所需形状的仓库(两个正方形或者一个正方形一个长方形)

能否建造正方形取决于是否有四个长度一样的木板
能否建造长方形取决于在减去四个长度一样的木板后,是否还至少两组两个长度一样的木板

用set1存放有四个长度一样的木板,用set2存放有两个长度一样的木板

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,q;
void solve() {cin>>n;set<int>s1,s2;map<int,int>mp;//桶计数for(int i=1;i<=n;i++){int x;cin>>x;mp[x]++;if(mp[x]>=2) s2.insert(x);if(mp[x]>=4) s1.insert(x);}cin>>q;while(q--){char ch;int x;cin>>ch>>x;if(ch=='+'){mp[x]++;if(mp[x]>=2) s2.insert(x);if(mp[x]>=4) s1.insert(x);}else{mp[x]--;if(mp[x]<2) s2.erase(x);if(mp[x]<4) s1.erase(x);}if(s1.size()>=2) cout<<"Yes"<<endl;else if(s1.size()==1){int x=*s1.begin();if(mp[x]>=8) cout<<"Yes"<<endl;else if(mp[x]>=6){if(s2.size()>=2) cout<<"Yes"<<endl;else cout<<"No"<<endl;}else{if(s2.size()<=2) cout<<"No"<<endl;else 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;
}

12.C. Phoenix and Towers

一共有n块积木,高度为hi(高度小于等于x)

将n块积木堆成m座塔,任意两座塔的高度差要小于等于x
坑点:YES,NO

如果不能建造,就输出NO,否则输出YES,并输出每个积木放在哪座塔中

可能平均分比较好

将n个积木分配到m组,使得任意两组差小于等于x
先升个序,然后按顺序循环放置,通过手玩,验证,发现任意两组是不会超的,应该是不会无解的

trick:

关于分配到几组的问题,就从第一组开始往后放就行了

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,m,x;
struct node{int a,idx;bool operator<(const node &W)const{return a<W.a;}
}q[N];
void solve() {cin>>n>>m>>x;for(int i=1;i<=n;i++) cin>>q[i].a,q[i].idx=i;sort(q+1,q+1+n);vector<int>ans(n+1);int mm=1;for(int i=1;i<=n;i++){ans[q[i].idx]=mm;mm++;if(mm==m+1) mm=1;}cout<<"YES"<<endl;for(int i=1;i<=n;i++) cout<<ans[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

13.A. Meximum Array

长度为n的数组a(数[0,n])

构造数组b
while(a不为空),在a中选择前k个个数,将MEX放入b的末尾
使得数组b字典序最大
贪心,优先选择MEX最大的

trick:

作为mex题目的一个案例

坑点:

贪心地一段一段截取,最后不能忘了最后一段

1.O(1)地得到x是否存在于后缀中:从左到右扫描, 维护每一个数字当前出现的次数和总的次数,如果当前数字出现次数和该数字总个数相等,说明后缀中没有该数字了,在这里是看当前的mex是否在后缀中出现过,如果没出现,那么就不会出现比当前mex更大的了,那么就贪心的结束

2.如何求mex:利用set维护mex,一边遍历,一边将其放入set中

while(s.count(mex)) mex++;

不用担心这样会超时,因为它是一边遍历一边放入set,一边维护mex,mex并不是每次从0开始加的

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;map<int,int>mp,mmp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++;set<int>s;vector<int>ans;int mex=0;for(int i=1;i<=n;i++){mmp[a[i]]++;s.insert(a[i]);while(s.count(mex)) mex++;if(mmp[mex]==mp[mex]){ans.push_back(mex);mex=0;s.clear();}}if(s.size()) ans.push_back(mex);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;
}

14.D. Productive Meeting

长度为n的数组a(数[0,2e5]),n大于等于2
ai表示社交能力,表示第i个人可以交谈几次

问一次会议最多可以交谈几次,并输出方案

贪心,优先消耗大的,如果先消耗小的,那么最大的只剩一个人了,没人和他交谈,那么次数都浪费掉了

坑点:

大于0的才能入队,因为入队是要去抵消的

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=2e5+10;
int n;
void solve() {cin>>n;priority_queue<PII>q;for(int i=1;i<=n;i++){int x;cin>>x;if(x) q.push({x,i});}vector<PII>ans;while(q.size()>1){auto t1=q.top();q.pop();auto t2=q.top();q.pop();ans.push_back({t1.second,t2.second});t1.first--;t2.first--;if(t1.first>0) q.push(t1);if(t2.first>0) q.push(t2);}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;
}

15.B2. Wonderful Coloring - 2

长度为n的序列,一共有k种颜色
给n个数着色

如果两个数相等,则不能涂一样的颜色
每种颜色用的次数必须相等(一共k种颜色,用的次数都要相等)

要求尽可能涂的数量最多

坑点:

题目读仔细,一开始不知道k种颜色都要用

trick:

本质上是一个分配问题,本题作为分配问题的一个案例

要将k组都分配,那么就从第1组开始到第k组,一组一组放

由于两个相同的数不能放在同一组,所以将所有数排个序,连续放在一起,这样只要不超过k个,就不会出现相同的数放在一起,具体做法是

	map<int,int>mp;vector<PII>ans;for(int i=1;i<=n;i++){mp[a[i]]++;if(mp[a[i]]<=k) ans.push_back({a[i],i});}
sort(ans.begin(),ans.end());

另外,排序后获取下标也不一定非得用结构体,也可以用以上方法,vector存放pair类型

包括下面也作为案例,共group组,每组k个,每组都通过加法取模来得到1到k

	int group=ans.size()/k;for(int i=0;i<group*k;i++){c[ans[i].second]=i%k+1;}
#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 c[N];
int n,k;
void solve() {cin>>n>>k;memset(c,0,sizeof c);for(int i=1;i<=n;i++) cin>>a[i];map<int,int>mp;vector<PII>ans;for(int i=1;i<=n;i++){mp[a[i]]++;if(mp[a[i]]<=k) ans.push_back({a[i],i});}sort(ans.begin(),ans.end());int group=ans.size()/k;for(int i=0;i<group*k;i++){c[ans[i].second]=i%k+1;}for(int i=1;i<=n;i++) cout<<c[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.C. Array Game

长度为n的数组a(数[1,1e18]) n大于等于2
操作:选取两个数,将两数的差的绝对值放在数组的末尾
k次操作

求数组a中最小值最小是多少

坑点:

没有想到可以再次选择两个同样的数,这样的话,三次一定可以得到0

其它只要讨论k为1以及k为2的情况

细细分析,为什么当时没想到,因为当时一开始就奔着贪心的思路去了,想着对两个差值最小的进行操作,实际上错了,但是方向已经偏了,所以,如果想到了贪心,一定要先验证,手玩造复杂极端的样例,如果不对,那么就立马纠正思路

或者说是如果是脑子里一看到就立马蹦出的思路,一定要验证,因为往往会被经验主义所带偏了方向

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e3+10;
int a[N];
int n,k;
void solve() {cin>>n>>k;int minn=1e18;for(int i=1;i<=n;i++) cin>>a[i],minn=min(minn,a[i]);if(k>=3){cout<<0<<endl;return;}sort(a+1,a+1+n);if(k==1){for(int i=2;i<=n;i++){minn=min(minn,a[i]-a[i-1]);}cout<<minn<<endl;return;}vector<int>ans;for(int i=1;i<n;i++){for(int j=i+1;j<=n;j++){minn=min(minn,abs(a[i]-a[j]));ans.push_back(abs(a[i]-a[j]));//操作一次得到的所有数,这是所有可能的情况,但是只能得到其中的一个}}sort(ans.begin(),ans.end());for(int i=1;i<=n;i++){int pos=lower_bound(ans.begin(),ans.end(),a[i])-ans.begin();if(pos<(int)ans.size()) minn=min(minn,abs(a[i]-ans[pos]));if(pos>0) minn=min(minn,abs(a[i]-ans[pos-1]));}cout<<minn<<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.C. Sum of Substrings

当1在最左端的时候,产生的贡献为1,当1在最右端的时候,产生的贡献为10,当1在中间的时候产生的贡献均为11

trick:

如果没有思路,就手玩造不同情况的复杂的极端的样例

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,k;
string s;
void solve() {cin>>n>>k;cin>>s;int l=-1,r=-1;map<char,int>mp;int cnt=0;for(int i=0;i<n;i++){if(s[i]=='1') cnt++;}for(int i=0;i<n;i++){if(s[i]=='1'){l=i;break;}}for(int i=n-1;i>=0;i--){if(s[i]=='1'){r=i;break;}}int res1=l,res2=n-1-r;if(cnt==0){cout<<0<<endl;return;}if(cnt==1){if(k>=res2) cout<<1<<endl;else if(k>=res1) cout<<10<<endl;else cout<<11<<endl;return;}if(k>=res1+res2) cout<<11+11*(cnt-2)<<endl;else if(k>=res2) cout<<1+11*(cnt-1)<<endl;else if(k>=res1) cout<<10+11*(cnt-1)<<endl;else cout<<11*cnt<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

18.D. Diverse Garland

n个灯
颜色已知,共R,G,B三种颜色
重新着色,使得相邻的灯颜色不同
问最少需要重新着色几个灯管

贪心,遍历,如果灯和前一个灯颜色相等,就要重新上色

trick:

一开始题目没读懂,题目一边读一边用自己的话记录,不存在读不懂的题目,问题在于读题时浮躁

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string tmp="RGB";
string s;
void solve() {cin>>n>>s;int ans=0;for(int i=1;i<n-1;i++){if(s[i]==s[i-1]){ans++;for(int j=0;j<3;j++){if(tmp[j]!=s[i-1]&&tmp[j]!=s[i+1]) s[i]=tmp[j];}}}if(s[n-1]==s[n-2]){ans++;for(int j=0;j<3;j++){if(tmp[j]!=s[n-2]) s[n-1]=tmp[j];}}cout<<ans<<endl;cout<<s<<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. Absolute Sorting

长度为n的数组a(数[1,1e8])n大于等于2
操作:选择一个整数x,将所有的ai替换为ai和x的差的绝对值,使得非降序
问有无这样的x,无解输出-1
如果有解,x在[0,1e9]

画一个数轴,从前往后遍历,如果前一个数小于后一个数,那么x必须选取在它们中间的左边
如果前一个数大于后一个数,那么x必须选取在它们中间的右边
一直取交集,如果交集为空,则无解
如果前一个数和后一个数相等,那么无论x取什么都可以,不能设区间

坑点:

如果前一个数和后一个数相等,那么无论x取什么都可以,不能设区间

trick:

1.没有思路就手玩,这题也是手玩做出来的

2.当出现差的绝对值时,可以画一个数轴,转化为点与点之间的距离,数形结合

3.非降序是整体的一个结果,我们聚焦到局部,如果相邻两两之间都非降序,那么整个序列自然就非降序了(整体->局部),但是注意必须始终保证朝一个方向遍历,只能修改后面的值,不能把前面的值修改

3.若要确定出一个数满足条件,我们可以以区间的思想,它可以在哪个范围内选择,在各种条件限制下,不断缩小区间,取交集

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];int l=0,r=1e9;for(int i=1;i<=n-1;i++){if(a[i]==a[i+1]) continue;if(a[i]<a[i+1]){int rr=(a[i]+a[i+1])/2;r=min(r,rr);}else{int ll=(a[i]+a[i+1]+1)/2;l=max(l,ll);}}if(l<=r) cout<<l<<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;
}

20.C. LIS or Reverse LIS?

长度为n的数组a(数[1,1e9])
beauty:数组a最长上升子序列的长度和最长下降子序列的长度取min
重新排列a,求最大的beauty

其中一个大,必然会导致另一个小,那么min也小
所以最好平均一下

与顺序无关,先排个序
然后如果每个数字都只有一个的话,那么肯定平均一下,答案为(数字种数+1)/2
但是如果每种数字不止一个,那么就可以把多余的数字往后放使得降序,这样的话,答案为max((数字的种数+1)/2,mp[digit]大于1的个数)
如果n为1的话,那么答案为1

大概思路是对的,但是应该分开来,cnt1为只有一个的数字,cnt2有多个数字的数字,答案为(cnt1+1)/2+cnt2
只有一个的数字应该取平均分,有多个的数字两边都可以分到
归结为两个集合,然后考虑哪些数字应该放在哪个集合中,以及顺序怎么放

trick:

作为一个案例

归结为两个集合,然后考虑哪些数字应该放在哪个集合中,以及顺序怎么放(这种题目,每一个数字只能选择其中一个集合放置)

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;map<int,int>mp;int maxn=0;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]++,maxn=max(maxn,a[i]);if(n==1){cout<<1<<endl;return;}int cnt1=0,cnt2=0;for(auto v:mp){if(v.second==1) cnt1++;else cnt2++;}cout<<(cnt1+1)/2+cnt2<<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. Not Assigning

一共有n个顶点
树的n-1条边

给n-1条边赋值,使得树成为质数树
质数树:每条边都是质数,相邻两条边的和也是质数

如果有解,权值范围在[1,1e5]
如果无解,输出-1

想到用质数2,3
如果一个顶点有三个分支,肯定不行,因为质数除了2是偶数,其它都是奇数,两个奇数相加为偶数就不不是质数了,所以只能一个2,再一个奇数质数,再一个2,这样2+2=4也不是质数了,所以一个顶点最多两个分叉,即度数最大为2,所以只有一条单向路才有解,直接2,3,2,3交替赋值即可

记录两个顶点所连边的序号
从叶子节点开始dfs,2,3,2,3交替赋值给当前边的序号

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=1e5+10;
vector<vector<int>>e(N);
int d[N];
int ans[N];
int n;
map<PII,int>mp;
void dfs(int u,int fa,int digit){ans[mp[{u,fa}]]=digit;for(auto v:e[u]){if(fa==v) continue;dfs(v,u,5-digit);}
}
void solve() {cin>>n;memset(d,0,sizeof d);memset(ans,0,sizeof ans);mp.clear();for(int i=1;i<=n;i++) e[i].clear();for(int i=0;i<n-1;i++){int u,v;cin>>u>>v;e[u].push_back(v);e[v].push_back(u);mp[{u,v}]=mp[{v,u}]=i+1;d[u]++;d[v]++;}for(int i=1;i<=n;i++){if(d[i]>=3){cout<<-1<<endl;return;}}int st;mp[{st,0}]=0;for(int i=1;i<=n;i++){if(d[i]==1){st=i;break;}}dfs(st,0,3);for(int i=1;i<n;i++) cout<<ans[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

22.C. awoo’s Favorite Problem

长度为n的字符串s和t,均由a,b,c组成
操作:将ab变成ba或者将bc变成cb
次数不限
问能否使s变成目标串t

a和c只能通过b进行移动,a和c彼此分割,所以将b去掉,字符串s和t应该是相等的

字符串s和t中字符a和c所处的对应的位置应该满足:s中的a不能在t中的a的前面,s中的c不能在t中的c的前面

trick:

给定串变成目标串问题

重点关注操作,手玩造不同情况的复杂的极端的样例,重点关注操作的对象,无非是ab和bc,将它们再分解成最基本的,无非是a,b,c三个基本单元,重点关注基本单元的变化

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<char,int>PII;
int n;
string s,t;
void solve() {cin>>n;cin>>s>>t;string tmp1,tmp2;vector<PII>ans1,ans2;for(int i=0;i<n;i++){if(s[i]!='b') tmp1+=s[i],ans1.push_back({s[i],i});if(t[i]!='b') tmp2+=t[i],ans2.push_back({t[i],i});}if(tmp1!=tmp2){cout<<"NO"<<endl;return;}for(int i=0;i<(int)ans1.size();i++){if(ans1[i].first=='a'){if(ans2[i].second<ans1[i].second){cout<<"NO"<<endl;return;}}else{if(ans2[i].second>ans1[i].second){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;
}

23.E. Vlad and a Pair of Numbers

加法恒等式:a+b=(a^b)+ 2 * (a&b)

由于a+b = 2 * (a^b),得a ^ b=2 * (a&b)= x

构造a和b

如果a^b为奇数,无解

从与以及异或本身的性质来看,如果a&b和a^b如果有任意一位同时为1,则 无解

a=(a&b)+(a^b)=x*(3/2)

b=a&b=x/2

trick:

1.加法恒等式:a+b=(a^b)+ 2 * (a&b)

2.如果a^b已知=x或者a&b已知=y,那么要确定a和b,只要进行分配1即可

比如说a&b中为1的位,a和b对应的该位均为1,然后其它位可一个为0,一个为1,也可以两个都为0,我们如果是构造的话,可以不用管0,所以构造a=b=a&b

再比如说ab中为1的位,a对应该位为1,b对应该位为0或者a对应该位为0,b对应该位为1,总之,一个为0,一个为1,我们如果是构造的话,只要满足一个为0,一个为1即可,那么我们可以把所有的1分给a,那么a为(a&b)或上(ab)

另外,(a&b)|(ab)=(a&b)+(ab),如果是或运算的话,只要没有位均为1,就可以直接加,无进位加法

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int x;
void solve() {cin>>x;if(x%2){cout<<-1<<endl;return;}if(x&(x/2)){cout<<-1<<endl;return;}cout<<(x|(x/2))<<' '<<x/2<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

24.C. Make Good

长度为n的数组a(数[0,1e9])
好:所有数的和等于所有数异或的两倍

最多添加三个不同的元素,使数组变好,一定有解

a+b+c=sum
abc=pre

a+b+c+pre=sum+pre
abcpre=prepre=0

a+b+c+pre+sum+pre=2*(sum+pre)

abcpre(sum+pre)=sum+pre

trick:

异或性质:

  1. x^0=x
  2. x^x=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;int sum=0,pre=0;for(int i=1;i<=n;i++) cin>>a[i],sum+=a[i],pre^=a[i];if(sum==2*pre){cout<<0<<endl<<endl;return;}cout<<2<<endl;cout<<pre<<' '<<sum+pre<<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.B. AND Sequences

长度为n的数组a(数[0,1e9])n大于等于2
良好序列:[a1,ai]全部与起来=[ai+1,an]全部与起来,对于i从1到n-1

对数组a重新排序,问几种序列是良好序列,mod 1e9+7

a[1]=a[2]a[3]…^a[n]

a[1]=a[1]a[1]=a[1]a[2]a[3]…a[n],

a[n]=a[1]a[2]a[3]^…a[n-1]

a[n]=a[n]a[n]=a[1]a[2]a[3]…a[n],

所以a[1]=a[n]=tmp=a[1]a[2]a[3]^…a[n]

由于a[1]和a[n]是所有数的与,所以中间可以随意放

trick:

1.对于位运算,更多的是一种数学式子上的东西,重在推导,所以把可得到的式子整整齐齐的一个一个列出来

2.关于位运算的题目,经常全部进行一次位运算

3.a & a = a

4.如果数组中的数全部都与起来等于tmp,那么tmp和任何一个数(数组当中的一个数)与,都等于tmp(根据第3点)

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10,mod=1e9+7;
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]]++;int tmp=a[1];for(int i=1;i<=n;i++) tmp&=a[i];int ans=1;ans=mp[tmp]*(mp[tmp]-1);ans%=mod;for(int i=n-2;i>=1;i--){ans=ans*i%mod;}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;
}

26.B. Suffix Operations

长度为n的数组(数[-5e8,5e8])n大于等于2
操作:将后缀所有元素加1或者减1
可以帮助他,可以修改一个整数(所有操作之前)
使得所有元素相等,问最少执行多少次操作

可以发现,一起变的数是同步增长或者同步减少的,对目标全部元素相等毫无帮助,所以一起进行操作的数应该是相等的,所以就从最后一个数开始,先让它变得和倒数第二个数相等,再让他们变得和倒数第三个数相等,一直到和第一个数相等

所以只要看相邻两个数的差,我们可以帮助他修改一个数,就看相邻两个数差最大的,变成0就行,不对,比如说
99 96 97 95,如果将96变成99,原本97到96需要1,96到99需要3,现在97到99需要2,所以应该看的是,将某个数改成相邻的数之后,相当于去掉这个数之后,差值减的最多的,所以只要分别枚举删除哪一个数

trick:
一边手玩一边关注被操作对象的变化,以它们之间的联系

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];int sum=0;int ans=1e18;for(int i=n;i>=2;i--) sum+=abs(a[i]-a[i-1]);ans=min(ans,sum-abs(a[1]-a[2]));ans=min(ans,sum-abs(a[n]-a[n-1]));for(int i=2;i<=n-1;i++){//枚举删除哪一个数ans=min(ans,sum-(abs(a[i]-a[i-1])+abs(a[i]-a[i+1]))+abs(a[i-1]-a[i+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;
}

27.D1. Mocha and Diana (Easy Version)

摩卡有节点1到n,戴安娜有节点1到n
初始边数不同,摩卡有m1条边,戴安娜有m2条边

问最多添加多少条边(两人的边必须同时添加,而且添加的边是一样的)

不在一个连通块就可以相连
数据比较小,直接暴力,O(n^2)

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=1010;
int p1[N],p2[N];
int n,m1,m2;
int find1(int x){if(p1[x]!=x) p1[x]=find1(p1[x]);return p1[x];
}
int find2(int x){if(p2[x]!=x) p2[x]=find2(p2[x]);return p2[x];
}
void solve() {cin>>n>>m1>>m2;for(int i=1;i<=n;i++) p1[i]=p2[i]=i;while(m1--){int u,v;cin>>u>>v;u=find1(u),v=find1(v);p1[u]=v;}while(m2--){int u,v;cin>>u>>v;u=find2(u),v=find2(v);p2[u]=v;}vector<PII>ans;for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){if(i==j) continue;int a1=find1(i),b1=find1(j);int a2=find2(i),b2=find2(j);if(a1!=b1&&a2!=b2){p1[a1]=b1;p2[a2]=b2;ans.push_back({i,j});}}}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;
}

28.C. Fillomino 2

共n个整数
n * n 的方阵,主对角线上放置1到n的排列
满足,1的连通块元素个数为1,2的连通块元素个数为2,以此类推

我们从上到下,数x,先往左看,如果放不了,就往下放,直到放置了刚好x个

trick:

如果样例中没有无解的情况,大概是没有无解的情况

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=510;
int p[N][N];
int n;
void solve() {cin>>n;memset(p,0,sizeof p);for(int i=1;i<=n;i++) cin>>p[i][i];for(int i=1;i<=n;i++){int x=i,y=i;int cnt=1;while(1){if(cnt==p[i][i]) break;if(y-1>=1&&p[x][y-1]==0){y--;p[x][y]=p[i][i];cnt++;if(cnt==p[i][i]) break;}else if(x+1<=n&&p[x+1][y]==0){x++;p[x][y]=p[i][i];cnt++;if(cnt==p[i][i]) break;}if(cnt==p[i][i]) break;}}for(int i=1;i<=n;i++){for(int j=1;j<=i;j++){cout<<p[i][j]<<' ';}cout<<endl;}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

29.A1. Dual (Easy Version)

长度为n的数组(数[-20,20])n小于等于20 数据比较小,考虑暴力

操作:将一个数加到另一个数上,可以自己加到自己

使非降序,最多50次操作就够了

如果都是负数,那么从后往前,后一个数加到前一个数上
如果都是正数,那么从前往后,前一个数加到后一个数上
如果有一个数是正数,那么就一直加自己加到大于20,然后每个数都加它,可以让每个数快速变成正数

trick:

如果操作对象有两个,考虑让一个定死,或者让两个都定死

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int,int>PII;
const int N=25;
int a[N];
int n;
void solve() {cin>>n;for(int i=1;i<=n;i++) cin>>a[i];vector<PII>ans;int maxn=-25;int maxi=0;for(int i=1;i<=n;i++){if(a[i]>maxn){maxn=a[i];maxi=i;}}if(maxn<=0){for(int i=n-1;i>=1;i--){ans.push_back({i,i+1});}}else{while(maxn<=20){ans.push_back({maxi,maxi});maxn*=2;}a[maxi]=maxn;for(int i=1;i<=n;i++){if(a[i]<0){ans.push_back({i,maxi});a[i]+=a[maxi];}}for(int i=1;i<=n-1;i++){ans.push_back({i+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;
}

30.C. No Prime Differences

n行m列的网格填入1到n * m
使得任意相邻两个数的差的绝对值不是质数,一定有解

行和列有一个为偶数,那么就可以1到n * m顺序填

如果m不是是质数,那么一个一个按顺序填

否则,先把所有偶数行输出,再把所有奇数行输出

trick:

1.注意一定要看数据范围,记下来,在这里n和m均大于等于4

2.构造题,往奇偶方向想,如果是矩阵构造题,往奇偶行方向想,在这里提供一种方法 ,先把所有偶数行输出,再把所有奇数行输出(相对于从1开始顺序填的矩阵)

3.矩阵构造题,一般是一边遍历一边输出,如果想把它们存在数组正确的位置上再全部输出比较困难

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
bool check(int x){if(x<=1) return false;for(int i=2;i<=x/i;i++){if(x%i==0) return false;}return true;
}
void solve() {cin>>n>>m;if(!check(m)){for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cout<<(i-1)*m+j<<' ';}cout<<endl;}}else{for(int i=2;i<=n;i+=2){for(int j=1;j<=m;j++) cout<<(i-1)*m+j<<' ';cout<<endl;}for(int i=1;i<=n;i+=2){for(int j=1;j<=m;j++) cout<<(i-1)*m+j<<' ';cout<<endl;}}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.C. Matching Arrays

长度为n的数组a和b,数[1,2*n]

重新排列b,使得ai大于bi的个数刚好为x,无解输出No

贪心,a中x个最大的和b中x个最小的匹配,先保证有x个,并不是a中最大的和b中最小的,而是a中x个最大的升个序,b中x个最小的升个序,然后按顺序匹配,剩下的同理,为什么要升序后按顺序比,可以通过反向思考来感受这样做的合理性

trick:

1.贪心的思考方式:通过反向思考来感受贪心是否合理

2.两两匹配问题

分为三种

第一种是序列内部进行两两匹配,比如说最小和最大匹配,次小和次大匹配

第二种是两个不同的序列进行匹配,比如说每次匹配差值最大的,要么序列a的最大和序列b的最小匹配,要么序列a的最小和序列b的最大匹配,再比如说要求是ai大于bi刚好有x个,那么需要a中最大的x个去和b中最小的x个匹配,保证有x个,但不是a的最大和b的最小匹配,而是分别升序,然后按顺序匹配,合理性通过反向思考感受

第三种是两个一样的序列进行匹配,一般是通过自身元素的两两交换来满足某个要求,本质上就是自身和自身匹配

#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,xx;
void solve() {cin>>n>>xx;vector<PII>ans;for(int i=0;i<n;i++){int x;cin>>x;ans.push_back({x,i});}multiset<int>s;multiset<int,greater<int>>s1,s2;for(int i=0;i<n;i++){int x;cin>>x;s.insert(x);}sort(ans.begin(),ans.end());reverse(ans.begin(),ans.end());for(int i=0;i<xx;i++){s1.insert(*s.begin());s.erase(s.begin());}while(s.size()){s2.insert(*s.begin());s.erase(s.begin());}for(int i=0;i<xx;i++){if(*s1.begin()>=ans[i].first){cout<<"No"<<endl;return;}a[ans[i].second]=*s1.begin();s1.erase(s1.begin());}for(int i=xx;i<n;i++){if(*s2.begin()<ans[i].first){cout<<"No"<<endl;return;}a[ans[i].second]=*s2.begin();s2.erase(s2.begin());}cout<<"Yes"<<endl;for(int i=0;i<n;i++) cout<<a[i]<<' ';cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

32.E. Add Modulo 10

长度为n的数组a(数[0,1e9])
操作:ai加上ai%10(可以多次对同一下标)
操作次数不限
问是否可以使所有元素相等

每次都只加个位,所以个位上的数字只要看个位即可

0
1->2->4->8->6->2
2->4->8->6->2
3->6->2->4->8->6->2
5->0

除了0和5特殊一些,其它均可以进入到2->4->8->6的循环中,并且从2到2会增加20,比如12->14->18->26->32

trick:

每次都只加个位,所以个位上的数字只要看个位即可

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;map<int,int>mp;for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]%10]++;if(mp[0]||mp[5]){for(int i=1;i<=n;i++){if(a[i]%10!=0&&a[i]%10!=5){cout<<"No"<<endl;return;}}for(int i=1;i<=n;i++){if(a[i]%10==5) a[i]+=5;}for(int i=2;i<=n;i++){if(a[i]!=a[i-1]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;return;}for(int i=1;i<=n;i++){while(a[i]%10!=2){a[i]+=a[i]%10;}a[i]%=20;}for(int i=2;i<=n;i++){if(a[i]!=a[i-1]){cout<<"No"<<endl;return;}}cout<<"Yes"<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

33.D. Bracket Coloring

长度为n的字符串

分组,可以跳着挑,使得每组括号轴对称
问最少分几组

trick:

对于括号序列判断合法的问题,我们常用前缀和判断,遇到左括号 +1 ,遇到右括号 −1

一个括号(左边是左括号,右边得有右括号和左括号抵消)是合法的 ⟺ 对于所有的前缀 s u m i sum_i sumi 都满足 s u m i sum_i sumi≥0 ,且最后 s u m i sum_i sumi=0 。

得到折线图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

而以下这一种是右括号在左边,右边有左括号和右括号抵消的合法括号序列的折线图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

于是得到以下这种,当sum大于等于0时,是第一种的合法括号序列,当sum小于等于0时,是第二种的合法括号序列

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int ans[N];
int n;
string s;
void solve() {cin>>n;cin>>s;int sum=0;int flag=0;for(int i=0;i<n;i++){if(s[i]=='(') sum++;else sum--;if(sum>0) ans[i]=1,flag|=1;else if(sum<0) ans[i]=2,flag|=2;else if(sum==0) ans[i]=ans[i-1];}if(sum){cout<<-1<<endl;return;}if(flag!=3){cout<<1<<endl;for(int i=0;i<n;i++) cout<<1<<' ';cout<<endl;return;}cout<<2<<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;
}

34.D1. Zero-One (Easy Version)

长度为n的01串a和b

操作:选择两个索引,01反转,如果两个索引相邻,那么代价为x,否则代价为y(x大于等于y)
操作不限次数
问a等于b的最小成本,无解输出-1

首先,需要反转的个数为奇数的话,那么无解

如果需要反转的个数为2的话,如果相邻的话,那么代价为min(x,2y),如果不相邻的话,那么代价为y
如果需要反转的个数大于2的话,那么代价为cnt/2
y

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,x,y;
string a,b;
void solve() {cin>>n>>x>>y;cin>>a>>b;vector<int>ans;for(int i=0;i<n;i++){if(a[i]!=b[i]) ans.push_back(i);}if(ans.size()%2){cout<<-1<<endl;return;}if(ans.size()==2){if(ans[1]==ans[0]+1){cout<<min(x,2*y)<<endl;}else cout<<y<<endl;}else{cout<<ans.size()/2*y<<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.C. Palindromifier

长度为n的只包含小写字母的字符串
操作:
二选一
1.将2到i反向加到s的前面
2.将i到n-1反向加到s的后面

将字符串变成回文串,最多操作30次,一定有解

有一种万能的方法:比如abcd,先将c移到最右边,变成abcdc,然后将bcd反向移到最左边,变成dcbabcdc,然后将第2个c移到最左边变成cdcbabcdc

trick:

重点关注操作对象,比如可以对连续一段进行操作,我们也可以考虑对单个进行操作

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;int len=s.size();cout<<3<<endl;cout<<'R'<<' '<<len-1<<endl;len++;cout<<'L'<<' '<<len-1<<endl;cout<<'L'<<' '<<2<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

36.B. Hossam and Friends

他有n个朋友,队列1到n
共m对互不相识的朋友

队列子段a到b如果都是好朋友,那么该子段就是好队列
问有几个子段是好队列

如果全部人都认识的话,那么第一个人作为区间右端点,有一个区间,第二个人作为区间右端点,有两个区间,…第i个人作为区间右端点,有i个区间

第i个人作为区间右端点,看它最多往左延申到哪里,看它左边包括它自己不相识的人的最大值取max

trick:

区间问题,往往可以将一个点作为右端点固定,然后看以它为右端点的区间

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int n,m;
void solve() {cin>>n>>m;memset(a,0,sizeof a);for(int i=0;i<m;i++){int x,y;cin>>x>>y;if(x>y) swap(x,y);a[y]=max(a[y],x);}int ans=0;for(int i=1;i<=n;i++){a[i]=max(a[i],a[i-1]);ans+=i-a[i];}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.C. Column Swapping

trick:

1.vector<vector>a(n,vector(m));可以直接cin>>a [i] [j]

2.如果要通过交换两个元素将一个序列排成非降序,可以另外放一个数组,先排好序,然后如果对应位置上的数不一样,那么这些位置需要交换

for(int i=0;i<n;i++){sort(b[i].begin(),b[i].end());}
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n,m;
void solve() {cin>>n>>m;vector<vector<int>>a(n,vector<int>(m));vector<vector<int>>b(n,vector<int>(m));for(int i=0;i<n;i++){for(int j=0;j<m;j++){cin>>a[i][j];b[i][j]=a[i][j];}}for(int i=0;i<n;i++){sort(b[i].begin(),b[i].end());}int l=-1,r=-1;for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(a[i][j]!=b[i][j]){l=j;break;}}for(int j=m-1;j>=0;j--){if(a[i][j]!=b[i][j]){r=j;break;}}if(l!=-1&&r!=-1) break;}if(l==-1&&r==-1){cout<<1<<' '<<1<<endl;return;}for(int i=0;i<n;i++){swap(a[i][l],a[i][r]);}for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(a[i][j]!=b[i][j]){cout<<-1<<endl;return;}}}cout<<l+1<<' '<<r+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;
}

38.A. Bear and Prime 100

交互题

任何一个合数都可以分解为若干个质数的乘积(唯一)

trick:
这是做到的第二个交互题,当作案例

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int prime[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,4,9,25,49};
void solve() {int cnt=0;for(int i=0;i<19;i++){cout<<prime[i]<<endl;cout.flush();string s;cin>>s;if(s[0]=='y') cnt++;}if(cnt<2) cout<<"prime"<<endl;else cout<<"composite"<<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.D. Row Major

构造一个长度为n的字符串
使得它排成任何矩阵的形状,都没有相邻的字符相等
字符个数要求最少
如果n为奇数,那么直接两个字符ab,一直循环
如果n为偶数,那么找到第一个x,满足x不是n的因数,x-1是n的因数,x个字符一直循环

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
void solve() {cin>>n;if(n%2){int flag=1;for(int i=0;i<n;i++){if(flag) cout<<'a';else cout<<'b';flag^=1;}}else{int num=1;for(int i=3;;i++){if(n%i&&n%(i-1)==0){num=i;break;}}int cnt=0;for(int i=0;i<n;i++){cout<<(char)('a'+cnt);cnt++;if(cnt==num) 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;
}

40.C. Fishingprince Plays With Array

长度为n的数组a
操作:
1.在数组a中选择一个m的倍数,将其变为m个ai/m
2.对于刚好连续m个相同的元素,用m*ai一个元素换掉m个元素
操作次数不限

问能否将a变成b

如果是m的倍数,那么就一直除以m,直到不是m的倍数,不能再分解,计算其最小单位的个数,以此类推

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=5e4+10;
int a[N],b[N];
int n,m;
int k;
void solve() {cin>>n>>m;for(int i=1;i<=n;i++) cin>>a[i];a[n+1]=2e9;cin>>k;for(int i=1;i<=k;i++) cin>>b[i];b[k+1]=2e9;vector<int>ans1,ans2;map<int,int>mp1,mp2;int cnt=0;int sum=1;while(a[1]%m==0){a[1]/=m;sum*=m;}cnt+=sum;for(int i=2;i<=n+1;i++){sum=1;while(a[i]%m==0){//分解为sum份a[i],a[i]已经分解成最小单位了a[i]/=m;sum*=m;}if(a[i]==a[i-1]) cnt+=sum;else{ans1.push_back(a[i-1]);int len=ans1.size()-1;mp1[len]=cnt;cnt=sum;}}cnt=0;sum=1;while(b[1]%m==0){b[1]/=m;sum*=m;}cnt+=sum;for(int i=2;i<=k+1;i++){sum=1;while(b[i]%m==0){//分解为sum份a[i],a[i]已经分解成最小单位了b[i]/=m;sum*=m;}if(b[i]==b[i-1]) cnt+=sum;else{ans2.push_back(b[i-1]);int len=ans2.size()-1;mp2[len]=cnt;cnt=sum;}}
//	for(int i=0;i<(int)ans1.size();i++) cout<<ans1[i]<<' ';
//	cout<<endl;
//	for(int i=0;i<(int)ans2.size();i++) cout<<ans2[i]<<' ';
//	cout<<endl;
//	for(int i=0;i<(int)ans1.size();i++) cout<<mp1[i]<<' ';
//	cout<<endl;
//	for(int i=0;i<(int)ans2.size();i++) cout<<mp2[i]<<' ';
//	cout<<endl;if(ans1.size()!=ans2.size()){cout<<"No"<<endl;return;}for(int i=0;i<(int)ans1.size();i++){if(ans1[i]!=ans2[i]||mp1[i]!=mp2[i]){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;
}

41.B. String Modification

长度为n的字符串
操作:对于所有长度为k的连续子串,从左到右依次颠倒

使得操作后的字典序最小,且选取的k尽量小

数据稍小,可以浅暴力,结果超时了,数据不算小,最多n^2
首先,需要将最小的字符放在第一个位置
所以先找到最小字符的位置

但是如果按照题目的步骤来的话必定会超时,然后通过找规律发现,最终结果是sk到sn,返回如果n和k奇偶性相同则加上sk-1sk-2…s1,否则加上s1s2…sk-1

trick:

如果按照题目的步骤很繁琐,必定会超时,那么可能可以手玩找规律直接一步得到经过那么多操作后的结果,做法是多手玩几个例子,得到最终的结果

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int n;
string s;
void solve() {cin>>n;cin>>s;char ch='z';for(int i=0;i<n;i++){if(ch>s[i]) ch=s[i];}vector<int>ans;for(int i=0;i<n;i++){if(s[i]==ch) ans.push_back(i);}
//	for(auto v:ans) cout<<v<<' ';
//	cout<<endl;string tmp;int k=1;for(int i=0;i<n;i++) tmp+='z'; for(auto v:ans){//第v个位置为最小字符int len=v+1;string ss;for(int i=v;i<n;i++) ss+=s[i];string sss;for(int i=0;i<v;i++) sss+=s[i];if(n%2==len%2) reverse(sss.begin(),sss.end());ss+=sss;if(tmp>ss){k=len;tmp=ss;}}cout<<tmp<<endl;cout<<k<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

42.C. Bricks and Bags

题解

1到n共n块转
ai为第i块转的重量
共三个集合,将n块砖分配到三个集合中,集合不能为空
分配完后,从三个集合中各取出一个砖块,得分为w1和w2的差的绝对值加上w2和w3的差的绝对值
然后设x为最少得分,问x最大为多少

集合分配问题,考虑将哪些数放在哪个集合中

考虑让两边和中间的差值大,中间放最大的或者最小的,但这样并不一定是最优的

trick:

比较巧妙,当作案例

1.想到贪心的思路,要先验证,构造极端复杂的样例,可以这样构造:差值超级大和差值超级小结合

2.贪心不可行的话,可提供的一种思路是一边遍历,一边将当前作为拐点,算出最优解,然后取所有中最优的,拐点结合集合分配问题,可以先将一种情况放好,然后进行移动,而且是连续一段进行移动

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int n;
void solve() {cin>>n;set<int>s;for(int i=1;i<=n;i++){cin>>a[i];s.insert(a[i]);}vector<int>ans;while(s.size()){ans.push_back(*s.begin());s.erase(s.begin());}int len=ans.size();if(len==1){cout<<0<<endl;return;}if(len==2){cout<<2*(ans[1]-ans[0])<<endl;return;}int res=ans[len-1]-ans[0]+ans[len-1]-ans[len-2];for(int i=1;i<len;i++){res=max(res,ans[i]-ans[0]+ans[i]-ans[i-1]);}res=max(res,ans[len-1]-ans[0]+ans[1]-ans[0]);for(int i=0;i<len-1;i++){res=max(res,ans[len-1]-ans[i]+ans[i+1]-ans[i]);}cout<<res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

43.D. Equalize Them All

操作:选择相邻的两个数,让第一个数加上或减去它们差的绝对值
问最少几次操作,可以让所有元素相等,一定有解

操作1的意思其实是将小的数变成大的数,操作2的意思其实是将大的数变成小的数
看哪个数最多,那么就变得全部和它一样

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
typedef pair<int,int>PII;
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]]++;}int num;int cnt=0;for(auto v:mp){if(cnt<=v.second){cnt=v.second;num=v.first;}}set<int>s;for(int i=1;i<=n;i++){if(a[i]==num) s.insert(i);}cout<<n-cnt<<endl;int l=1;int en=(*(--s.end()));while(s.size()){int pos=*s.begin()-1;while(pos>=l){if(a[pos]<num) cout<<1<<' '<<pos<<' '<<pos+1<<endl;else cout<<2<<' '<<pos<<' '<<pos+1<<endl;pos--;}l=*s.begin()+1;s.erase(s.begin());}if(en<n){for(int i=en+1;i<=n;i++){if(a[i]<num) cout<<1<<' '<<i<<' '<<i-1<<endl;else cout<<2<<' '<<i<<' '<<i-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;
}

44.A. Grid game

竖着的在第一列抵消,横着的在第三行抵消

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
string s;
void solve() {cin>>s;int n=s.size();bool ok1=false,ok2=false;//ok1表示竖着的有没有放在(1,1),ok2表示横着的有没有放在(3,3)for(int i=0;i<n;i++){if(s[i]=='0'){if(!ok1) cout<<1<<' '<<1<<endl,ok1=true;else cout<<3<<' '<<1<<endl,ok1=false;}else{if(!ok2) cout<<3<<' '<<3<<endl,ok2=true;else cout<<3<<' '<<1<<endl,ok2=false;}}
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

45.B. Pasha and String

关于对称轴翻转,只要看左边一半就行,记录翻转的次数

利用差分和前缀和

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int b[N];
int sum[N];
string s;
int m;
void solve() {cin>>s;int n=s.size();s=' '+s;cin>>m;for(int i=0;i<m;i++){int x;cin>>x;a[i]=x;if(x>n/2) x=n-x+1;b[x]++;}for(int i=1;i<=n/2;i++) sum[i]=sum[i-1]+b[i];for(int i=1;i<=n/2;i++){if(sum[i]%2==1) swap(s[i],s[n-i+1]);}for(int i=1;i<=n;i++) cout<<s[i];cout<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

46.C. Theofanis’ Nightmare

贪心

乘i看作加i次

考虑从前往后,什么时候断开,当后缀和大于0那么就断开,如果后缀和小于0断开是不优的,因为放在后面,让负的多加了

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e5+10;
int a[N];
int last[N];
int n;
void solve() {cin>>n;memset(last,0,sizeof last);for(int i=1;i<=n;i++) cin>>a[i];last[n]=a[n];for(int i=n-1;i>=1;i--) last[i]=a[i]+last[i+1];vector<int>ans;int sum=a[1];for(int i=2;i<=n;i++){if(last[i]>0){ans.push_back(sum);sum=a[i];}else sum+=a[i];}if(sum) ans.push_back(sum);int res=0;for(int i=0;i<(int)ans.size();i++){res+=(i+1)*ans[i];}cout<<res<<endl;
}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;cin>>t;while(t--) {solve();}return 0;
}

47.C. Diverse Matrix

构造大小最小的多样性矩阵,无解输出0

1行1列无解

特判行为1和列为1的情况

发现一种构造方式:第一行放r+c的后c个数,然后第i行是第1行的i倍

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
int r,c;
void solve() {cin>>r>>c;if(r==1&&c==1){cout<<0<<endl;return;}if(r==1){for(int i=2;i<=c+1;i++) cout<<i<<' ';cout<<endl;return;}if(c==1){for(int i=2;i<=r+1;i++) cout<<i<<endl;return;}int m=r+c;int st=m-c+1;for(int i=1;i<=r;i++){for(int j=st;j<=m;j++){cout<<i*j<<' ';}cout<<endl;}}
signed main() {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);int t=1;
//    cin>>t;while(t--) {solve();}return 0;
}

48.C. Complementary XOR

长度为n的01串a和b
一次操作:
选择l,r
1.串a[l,r]01反转
2.串b[1,l-1]01反转或者[r+1,n]01反转,如果存在就必须01反转
使得两个串全部都变成0,最多操作n+5次,无解输出NO

感觉这个操作限制太大,某种序列才能成功全部变成0
串b,要么和a相等,要么和a相反才可以
每次操作后,串a和串b要么完全相同,要么完全相反
那么就把串a先全部变成0,如果串b已经全部为0了,那么结束,如果串b全为1,那么(1,1),(2,n),(1,n)

trick:

如果整个序列并不是一段一段连续的,而是交替的,那么对一段进行操作不好操作,考虑对单个进行操作

#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
typedef pair<int, int>PII;
int n;
string a, b;
void solve() {cin >> n;cin >> a >> b;string c = a;for (int i = 0; i < n; i++) {if (c[i] == '1') c[i] = '0';else c[i] = '1';}if (b != a && b != c) {cout << "NO" << endl;return;}bool ok=false;if(a[0]=='1') ok=true;vector<PII>ans;for(int i=0;i<n;i++){if(a[i]=='1') ans.push_back({i+1,i+1});}int cnt=ans.size();if(ok) cnt--;if(cnt%2){if(b[0]=='0') b[0]='1';else b[0]='0';}if(b[0]=='1'){ans.push_back({1,1});ans.push_back({2,n});ans.push_back({1,n});}cout<<"YES"<<endl;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;
}

49.C. Set Construction

n * n的01矩阵
bij为第i行第j列

构造A1,A2,…An,满足如果Ai是Aj的适当子集,那么bij=1,否则bij=0
一定有解

A2->A1->A4
A3->A4
感觉有点像拓扑,但具体感觉又不会
可以拓扑,首先让点i初始都只有i,然后拓扑排序,子集中有的,全都给它的超集

trick:

很妙,让点i初始都只有i,当作案例

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1010;
char b[N][N];
int d[N];
int n;
set<int>s[N];
void solve() {cin>>n;memset(d,0,sizeof d);vector<vector<int>>e(n+1);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){cin>>b[i][j];if(b[i][j]=='1'){e[i].push_back(j);d[j]++;} }}for(int i=1;i<=n;i++) s[i].clear();for(int i=1;i<=n;i++) s[i].insert(i);queue<int>q;for(int i=1;i<=n;i++){if(!d[i]) q.push(i);}while(q.size()){int t=q.front();q.pop();for(auto v:e[t]){//t为子集,v为超集for(auto x:s[t]) s[v].insert(x);d[v]--;if(!d[v]) q.push(v);}}for(int i=1;i<=n;i++){cout<<s[i].size()<<' ';for(auto v:s[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;
}

50.B. Arrays Sum

第一次我们选择前k段,之后每一次都要先选前面的若干个0,这样一次就只能选k−1段。

trick:

将一个数分解x为若干个数的和,构造题的话随我们自己构造,我们可以分解为一个x,然后其它全补0,此时想补几个0就补几个0

#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;for(int i=1;i<=n;i++) cin>>a[i];int cnt=1;//一共有cnt种数字for(int i=2;i<=n;i++){if(a[i]!=a[i-1]) cnt++;}if(k==1&&cnt!=1) cout<<-1<<endl;else if(k==1) cout<<1<<endl;else{int ans=0;if(cnt>=k) cnt-=k,ans++;ans+=cnt/(k-1)+(cnt%(k-1)!=0);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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/676793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java编程构建高效二手交易平台

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

二、Mybatis相关概念

1.对象/关系数据库映射&#xff08;ORM) ORM全称Object/Relation Mapping&#xff1a;表示对象-关系映射的缩写ORM完成面向对象的编程语言到关系数据库的映射。当ORM框架完成映射后&#xff0c;程序员既可以利用面向对象程序设计语言的简单易用性&#xff0c;又可以利用关系数…

【JS逆向三】逆向某某网站的sign参数,并模拟生成仅供学习

逆向日期&#xff1a;2024.02.06 使用工具&#xff1a;Node.js 类型&#xff1a;webpack 文章全程已做去敏处理&#xff01;&#xff01;&#xff01; 【需要做的可联系我】 可使用AES进行解密处理&#xff08;直接解密即可&#xff09;&#xff1a;AES加解密工具 1、打开某某…

MySQL篇----第十七篇

系列文章目录 文章目录 系列文章目录前言一、对于关系型数据库而言,索引是相当重要的概念,请回答有关索引的几个问题二、解释 MySQL 外连接、内连接与自连接的区别三、Myql 中的事务回滚机制概述前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分…

来自谷歌的新年礼物!速来免费领取2个月谷歌Gemini Advanced会员!价值280元!对标ChatGPT Plus!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【iOS】——使用ZXingObjC库实现条形码识别并请求信息

文章目录 前言一、实现步骤二、扫描界面和扫描框的样式1.扫描界面2.扫描框 三、实现步骤 前言 ZXing库是一个专门用来解析多种二维码和条形码&#xff08;包括包括 QR Code、Aztec Code、UPC、EAN、Code 39、Code 128等&#xff09;的开源性质的处理库&#xff0c;而ZingObjC库…

网络编程..

1.互联网 有了互联网的出现 我们就可以足不出户的实现看电影、购物等等操作 我们认知中可能的互联网模型 较为真实的互联网模型 那么数据是如何从一个设备传递到另外一个设备的呢&#xff1f; 2.网络互联模型 统共有三种&#xff1a; 3.TCP/IP协议 TCP/IP是一群协议 里面…

【Linux笔记】动静态库的封装和加载

一、静态库的封装 我们在学习C语言阶段其实就已经知道一个可执行程序的形成过程分为预处理、编译、汇编、链接这四个阶段&#xff0c;而且也知道我们程序中使用的各种库其实是在链接的阶段加载的。 可我们那时候并不知道库是怎么被加载的&#xff0c;或者库是怎么形成的&…

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(12)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述&#xff08;11&#xff09; 4.2 PCIe体系结构的组成部件 PCIe总线作为处理器系统的局部总线&#xff0c;其作用与PCI总线类似&#xff0c;主要目的是为了连接处理器系统中的外部设备…

CNN应用Keras Tuner寻找最佳Hidden Layers层数和神经元数量

介绍&#xff1a; Keras Tuner是一种用于优化Keras模型超参数的开源Python库。它允许您通过自动化搜索算法来寻找最佳的超参数组合&#xff0c;以提高模型的性能。Keras Tuner提供了一系列内置的超参数搜索算法&#xff0c;如随机搜索、网格搜索、贝叶斯优化等。它还支持自定义…

.NET高级面试指南专题六【线程安全】5种方法解决线程安全问题

前言 多线程编程相对于单线程会出现一个特有的问题&#xff0c;就是线程安全的问题。所谓的线程安全&#xff0c;就是如果你的代码所在的进程中有多个线程在同时运行&#xff0c;而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的&#xff0c;而且…

备战蓝桥杯---动态规划(基础1)

先看几道比较简单的题&#xff1a; 直接f[i][j]f[i-1][j]f[i][j-1]即可&#xff08;注意有马的地方赋值为0&#xff09; 下面是递推循环方式实现的AC代码&#xff1a; #include<bits/stdc.h> using namespace std; #define int long long int a[30][30]; int n,m,x,y; …

机器学习---学习与推断,近似推断、话题模型

1. 学习与推断 基于概率图模型定义的分布&#xff0c;能对目标变量的边际分布&#xff08;marginal distribution&#xff09;或某些可观测变量 为条件的条件分布进行推断。对概率图模型&#xff0c;还需确定具体分布的参数&#xff0c;称为参数估计或学习问 题&#xff0c;…

AcWing 1224 交换瓶子(简单图论)

[题目概述] 有 N 个瓶子&#xff0c;编号 1∼N&#xff0c;放在架子上。 比如有 5 个瓶子&#xff1a; 2 1 3 5 4 要求每次拿起 2 个瓶子&#xff0c;交换它们的位置。 经过若干次后&#xff0c;使得瓶子的序号为&#xff1a; 1 2 3 4 5 对于这么简单的情况&#xff0c;显然&a…

第5章 数据库操作

学习目标 了解数据库&#xff0c;能够说出数据库的概念、特点和分类 熟悉Flask-SQLAlchemy的安装&#xff0c;能够在Flask程序中独立安装扩展包Flask-SQLAlchemy 掌握数据库的连接方式&#xff0c;能够通过设置配置项SQLALCHEMY_DATABASE_URI的方式连接数据库 掌握模型的定义…

Kafka集群安装与部署

集群规划 准备工作 安装 安装包下载&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1BtSiaf1ptLKdJiA36CyxJg?pwd6666 Kafka安装与配置 1、上传并解压安装包 tar -zxvf kafka_2.12-3.3.1.tgz -C /opt/moudle/2、修改解压后的文件名称 mv kafka_2.12-3.3.1/ kafka…

【C++】引用与内联

个人主页 &#xff1a; zxctsclrjjjcph 文章封面来自&#xff1a;艺术家–贤海林 如有转载请先通知 文章目录 1. 前言2. 引用2.1 引用概念2.2 引用使用场景2.3 引用特性2.4 引用和指针的区别2.5 传值、传引用效率比较2.5.1 值和引用的作为返回值类型的性能比较 3. 内联函数3.1 …

Ansible copy模块 复制文件使用 主服务器 给副服务器 复制文件使用 指定文件权限 覆盖备份等

目录 参数复制文件然后进行同时复制操作 给定内容生成文件&#xff0c;并制定权限验证 关于覆盖先查看当前内容覆盖并备份查看文件权限 还有有没有备份查看文件内容 参数 这个模块用于将文件复制到远程主机&#xff0c;同时支持给定内容生成文件和修改权限等。   其相关选项…

【iOS分类、关联对象】如何使用关联对象给分类实现一个weak的属性

如何使用关联对象给分类实现一个weak的属性 通过关联对象objc_setAssociatedObject中的策略policy可知&#xff0c;并不支持使用weak修饰对象属性&#xff1a; typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN 0, //assignOBJC_ASSOCIATION…

Android:Volley框架使用

3.15 Volley框架使用 Volley框架主要作为网络请求,图片加载工具。当应用数据量小、网络请求频繁,可以使用Volley框架。 框架Github地址:https://github.com/google/volley Volley框架的简单使用,创建项目Pro_VolleyDemo。将Github上下载Volley框架源代码,volley-master.zi…