Codeforces Round #744(Div.3)简训
导语
日常
涉及的知识点
思维,模拟,堆
链接:Codeforces Round #744(Div.3)
题目
A Casimir’s String Solitaire
题目大意:给出一个只有字符ABC的字符串,每次只能同时删除任意位置的一个A和一个B或一个B和一个C,问最后能否删成空串
思路:统计B的数量是否等于A和C之和即可
代码
#include <bits/stdc++.h>
using namespace std;
int t,n;
int main() {
cin >>t;
while(t--) {
char s[100]= {'\0'};
scanf("%s",s);
int len=strlen(s),a=0,b=0,c=0;
for(int i=0; i<len; i++)
if(s[i]=='A')a++;
else if(s[i]=='B')b++;
else c++;
if(b==a+c)
cout<<"YES\n";
else
cout <<"NO\n";
}
return 0;
}
B Shifting Sort
题目大意:给出一段正整数序列, a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an,定义一次操作 l , r , d l,r,d l,r,d,意思为将下标l ~ r的元素循环移位d位,现给定序列,输出一种能在n次操作之内使得序列升序的方案,逐步输出
思路:每次排序将未排好且最小的数放在该放的位置即可,暴力查找然后暴力交换
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int n,a[121],l[121],r[121],t;
signed main() {
cin >>t;
while(t--) {
cin >>n;
for(int i=0; i<n; i++)cin >>a[i];
int cnt=0;
for(int i=0; i<n-1; i++) {//查看排到了第几个数
int pos=i;//预存位置
for(int j=i+1; j<n; j++)//找到当前要排的值
if(a[j]<a[pos])pos=j;
if(pos>i) {//如果位置不对
l[++cnt]=i+1;//记录操作边界
r[cnt]=pos+1;
int tmp=a[pos];
for(int j=pos; j>i; j--)//复制
a[j]=a[j-1];
a[i]=tmp;
}
}
cout <<cnt<<endl;
for(int j=1; j<=cnt; j++)
cout <<l[j]<<" "<<r[j]<<" "<<r[j]-l[j]<<endl;
}
return 0;
}
C Ticks
题目大意:略
思路:直接暴力遍历,从最后一行开始,查找已经涂色的点,向上延伸查看能否构成需要的图形,能构成就将点标记,最后判断是否有涂色但未标记的点即可
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int n,m,k,t;
int status[20][20];//标记状态
signed main() {
cin >>t;
while(t--) {
cin >>n>>m>>k;
memset(status,0,sizeof(status));
for(int i=0; i<n; i++)//记录已经涂色的点
for(int j=0; j<m; j++) {
char ch;
cin >>ch;
if(ch=='*')
status[i][j]=1;
}
for(int i=n-1; i>-1; i--)//从最后一行开始向上搜
for(int j=0; j<m; j++) {//从左到右
if(status[i][j]==0)continue;//遇到了空点直接跳过
int len=0;
while (j>len&&j+len+1<m&&i>len) {//向上拓展,查看是否能组成图形
if (status[i-len-1][j-len-1] == 0||status[i-len-1][j+len+1] == 0)
break;//不能组成则中断
len++;//记录能组成的个数
}
if(len>=k)
for(int d=0; d<=len; d++)//标记已经拿来组成的点
status[i-d][j-d]=2, status[i-d][j+d]=2;
}
bool flag= 1;
for(int i=0; i<n; i++)
for(int j=0; j<m; j++)
if(status[i][j]==1) {
flag=0;
break;
}
cout <<(flag?"YES":"NO")<<endl;
}
return 0;
}
D Productive Meeting
题目大意:n个人,每个人有说话次数,每次交流需要两个人,每产生一次交流需要消耗两个人一次说话次数,次数为零不能和他人交流,询问最多的交流次数
思路:做的时候直接升序排序,然后贪心选择最大与次大,把次大耗完,然后再把最大耗完,但是对2 2 2这种序列算出的答案是错的,在此基础上可以将思路改进:每次选择次数最大与次大,相互-1,然后放回队列,循环往复,这样的思路可以杜绝对2 2 2 这一类序列产生的错误
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int t,n,l[maxn],r[maxn];
typedef pair<int,int>pr;
signed main() {
cin >>t;
while(t--) {
priority_queue<pr,vector<pr>,less<pr>>q;
cin >>n;
int p,ans=0;
for(int i=1; i<=n; i++) {
cin >>p;
if(p)
q.push({p,i});
}
while(!q.empty()) {
pr x=q.top();
q.pop();
if(q.empty())break;
pr y=q.top();
q.pop();
if(x.first-1>0)
q.push({x.first-1,x.second});
if(y.first-1>0)
q.push({y.first-1,y.second});
l[++ans]=x.second;
r[ans]=y.second;
}
cout <<ans<<endl;
for(int i=1; i<=ans; i++)
cout <<l[i]<<" "<<r[i]<<endl;
}
return 0;
}
E1 Permutation Minization by Deque
题目大意:略
思路:贪心的构造双端队列即可
代码
#include <bits/stdc++.h>
using namespace std;
int t,n;
int main() {
cin >>t;
while(t--) {
cin >>n;
deque<int>q;
while(n--) {
int x;
cin >>x;
if(q.empty()||x>q.front())q.push_back(x);
else q.push_front(x);
}
while(!q.empty()) {
cout <<q.front()<<" ";
q.pop_front();
}
cout <<endl;
}
return 0;
}
E2 Array Optimization by Deque
题目大意:略
思路:对于第一个元素的取舍是没有首尾之分的,因为只有一个元素
假设现在已经固定好了
i
−
1
i-1
i−1个元素,得到的序列是
[
d
1
,
…
,
d
i
−
1
]
[d_1,\dots,d_{i-1}]
[d1,…,di−1],现在要插入
a
i
a_i
ai,那么显然最后结果只有两种:
[
a
i
,
d
1
,
…
,
d
i
−
1
]
,
[
d
1
,
…
,
d
i
−
1
,
a
i
]
[a_i,d_1,\dots,d_{i-1}],[d_1,\dots,d_{i-1},a_i]
[ai,d1,…,di−1],[d1,…,di−1,ai],在这里可以观察到一个性质,如果
a
i
<
d
1
a_i<d_1
ai<d1,那么第二种插入一定会使逆序对数增加,第一种插入一定使总逆序数不变,反过来,如果
a
i
》
d
1
a_i》d_1
ai》d1,那么第一种插入一定会使逆序对数增加,第二种插入一定使总逆序数不变,这里就可以推得思路:将即将插入的数与当前序列的第一个值比较,看哪种情况下得到的逆序数少即可
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10;
int t,n,tree[2*maxn],a[maxn],b[maxn];
void update(int x) {
for(; x<=n; x+=-x&x)tree[x]++;
}
int sum(int x) {
int acc=0;
for(; x; x-=x&-x)acc+=tree[x];
return acc;
}
signed main() {
cin >>t;
while(t--) {
cin >>n;
int ans=0,len;
memset(tree,0,sizeof(tree));
for(int i=1; i<=n; i++) {
cin >>a[i];
b[i]=a[i];
}
sort(a+1,a+1+n);
len=unique(a+1,a+1+n)-a-1;
for(int i=1; i<=n; i++)//离散化,因为有负数和0
b[i]=lower_bound(a+1,a+1+len,b[i])-a;
for(int i=1; i<=n; i++) {
update(b[i]);
ans+=min(sum(n)-sum(b[i]),sum(b[i]-1));//查看放在前面或者后面的最小值
}
cout <<ans<<endl;
}
return 0;
}