众所周知,codeforce div2AB两题以思维题为主,极少涉及算法知识。对于新手来说,能够快速并准确地解决B题是上分的关键。当AB题的写题量达到一定程度时,可以发现所谓思维题也有套路可循。据此,笔者将套路归纳为几大块,并收纳了一些具有一定代表性的题目。
本题单持续更新。
模拟
这里放上一些对模拟码力要求较高而解题性质较明显的题目。
732(div2) A.AquaMoon and Two Arrays
题意:虽然是A题,但还是有点码力要求的 给两个序列,可以对第一个序列进行任意次操作,每次操作可选取两个数,使其中一个数加一,另一个数减一,问能否进行小于等于100次的操作,使第一个序列变成第二个序列,并将操作过程输出。
思路:纯模拟+贪心。将所有数分为两部分,要加的和要减的,把要加的差减去要减的,可以发现最后两差分数组所有数必为零,不为零输出-1。
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
struct Node{
int x,y;
};
bool cmp(Node x,Node y){
return x.y < y.y;
}
signed main(){
IOS
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1],b[n+1];
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n;i++) cin>>b[i];
Node d1[n+1],d2[n+1];
//d1存要加的,d2存要减的
//处理d1 d2数组 y存差值 x存下标
int cnt = 0,snt = 0;
for (int i=1;i<=n;i++){
int d = a[i] - b[i];
if (d<0){
cnt++;
d1[cnt].y = -d;
d1[cnt].x = i;
}
else if (d>0){
snt++;
d2[snt].y = d;
d2[snt].x = i;
}
}
//贪心 根据值排序
sort(d1+1,d1+cnt+1,cmp);
sort(d2+1,d2+snt+1,cmp);
vector<pair<int,int>>op;
//op存操作过程
bool flag = 0;
//flag判断能否成功变成
//模拟部分
for (int i=1;i<=cnt;i++){
int dd = d1[i].y;
//取出一个d1 然后暴力遍历d2
for (int j=1;j<=snt;j++){
if (d2[j].y>=dd){
//d2 比 dd大 直接减
d2[j].y -= dd;//d2减去dd贡献
for (int k=1;k<=dd;k++){
op.push_back({
d2[j].x,d1[i].x});
}//存操作数
dd = 0;//dd清零
break;
}
else {
for (int k=1;k<=d2[j].y;k++){
op.push_back({
d2[j].x,d1[i].x});
}
dd -= d2[j].y;//dd减去贡献
d2[j].y = 0;//d2变为0
}
}
if (dd){
//d1经过遍历d2后都不能变成0 说明无法变成
flag = 1;
break;
}
}
//如果可以变成第二个序列,d2数组在操作后必为零
for (int i=1;i<=snt;i++){
if (d2[i].y){
flag = 1;//不为零就说明变成不了
break;
}
}
//输出部分
if (flag) cout<<-1<<endl;
else {
cout<<op.size()<<endl;
for (auto it:op){
cout<<it.first<<" "<<it.second<<endl;
}
}
}
}
751(div2) B. Divine Array
题意:给一串数,进行无数次操作,每次操作让每个数变成它出现的次数。进行q次询问,询问第x下标第k轮操作的值
思路:模拟+思维。对于无数次操作的题目,显然是去找一个“不动结点”,即发现哪次操作后数组值不会改变。手模一遍后发现,当每个数的数值等于它出现次数时,它就不变了。故用桶模拟出这个过程即可。
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
using namespace std;
signed main(){
IOS
int t;cin>>t;
while (t--){
int n;cin>>n;
int a[n+1];
map<int,int>mp;
int m[n+1][n+1];
//m[i][k]第k次操作第i个数的值
//mp记录每个数出现次数
for (int i=1;i<=n;i++){
cin>>a[i];
m[i][0] = a[i];
mp[a[i]]++;
}
int cnt = 0;
while (1){
bool flag = 0;
cnt++;
//cout<<"cnt = "<<cnt<<endl;
for (int i=1;i<=n;i++){
m[i][cnt] = mp[m[i][cnt-1]];
//cout<<m[i][cnt]<<" ";
//mp[mmp[a[i]]]++;
}
mp.clear();
for (int i=1;i<=n;i++){
mp[m[i][cnt]]++;
}
for (int i=1;i<=n;i++){
if (mp[m[i][cnt]]!=m[i][cnt]) flag = 1;
}
//cout<<endl;
if (flag) continue;
else break;//当所有数等于出现次数时 不会再改变 跳出循环
}
int q;cin>>q;
while (q--){
int x,k;cin>>x>>k;
if (k>cnt) k = cnt;
cout<<m[x][k]<<endl;
}
//cout<<"......."<<endl;
}
}
705(div2) B. Planet Lapituletti
题意:自定义小时进制h,分钟进制m,给出当前时间,求最近的在镜子里合法的未来时间。
思路:模拟+简单规律。可以发现镜子里的合法时间仅包括0,1,2,5,8五个数字,故暴力枚举所有合法时间和其镜子里的映射时间,判断两者是否均符合进制,记录离当前时间最近的时间即可。
#include <bits/stdc++.h>
#define INF 0x7f7f7f7f
using namespace std;
char s[] = {
'0','1','2','5','8'};
string s1[30];
int cnt;
int at(string s){
//计算现实时间
int ans = 0;
for (int i=0;i<s.si