Codeforces Round 719 Div. 3 除F2题外补题报告
得分情况
A题AC
B题AC
C题TLE在样例2
(题简单,脑袋没转过弯来)
补题情况
全部AC
错题分析
C题
题目大意
构造一个n∗n的矩阵,使得相邻的两个格子填的数不能相邻。
初次思路
暴力枚举。
正解思路
我们可以把奇偶分开填入矩阵,可知,除了2*2无解,别的情况都行。
正解代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int k=1;
int n;
scanf("%d",&n);
if(n==1){
printf("1\n");
continue;
}
if(n==2){
printf("-1\n");
continue;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",k);
k+=2;
if(k>n*n){
k=2;
}
}
printf("\n");
}
}
return 0;
}
错误原因
题意理解错了。
D题
题目大意
给出T组序列a,长度为N,询问有多少对 ( i , j ) (i,j) (i,j)满足 i < j i<j i<j且 a j − a i = j − i a_j −a_i =j−i aj−ai=j−i。
初次思路
暴力枚举。
正解思路
将 a j − a i = j − i a_j −a_i =j−i aj−ai=j−i变形为 a j − j = a i − i a_j −j =a_i−i aj−j=ai−i,然后统计值与下标的差,再用排列组合公式求出数量—— n ∗ ( n − 1 ) / 2 n*(n-1)/2 n∗(n−1)/2,最后求和。
正解代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int vis[400005];
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(vis,0,sizeof vis);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
vis[x-i+n]++;
}
long long ans=0;
for(int i=0;i<=2*n;i++){
ans+=1ll*vis[i]*(vis[i]-1)/2;
}
printf("%lld\n",ans);
}
return 0;
}
错误原因
没有想到对原式进行变形。
E题
题目大意
一共有 t 组数据,每组数据第一行为 n ,为字符串长度,下一行为一个字符串(只有 ’ . ’ 和 ’ * '字符,每次可以向右或者向左移动 ‘ * ’ 字符,求把所有的 ’ * ’ 字符连起来的最小移动次数。
初次思路
暴力模拟。
正解思路
中间的羊是羊群的中心,因此让羊往中心靠拢最优,所以统计其它羊靠过来的距离最优。
正解代码
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int sheep[1000005];
int main(){
int t;
cin>>t;
while(t--){
long long n,cnt=0,step=0;
string s;
cin>>n>>s;
for(int i=0;i<n;i++){
if(s[i]=='*'){
sheep[++cnt]=i;
}
}
int mid=(1+cnt)/2;
int len_left=1,len_right=1;
for(int i=1;i<=cnt;i++){
if(sheep[mid]>sheep[i]){
step+=sheep[mid]-sheep[i]-len_left++;
}
if(sheep[mid]<sheep[i]){
step+=sheep[i]-sheep[mid]-len_right++;
}
}
cout<<step<<"\n";
}
return 0;
}
F1题
题目大意
交互题。给定 n,t,k(由于这里是简单版,保证 t=1,即t没啥用),有一个长度为n的标号为1⋯n 的仅含01的数组,你每次可以询问l到r的和,需要得出数组中第k个0的下标。最多询问20次。
正解思路
可以用二分枚举位置,通过询问结果来判断前面有多少个0,如果0的数量大于等于k,说明mid要变小,l=mid+1,反之,r=mid。
正解代码
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int sheep[1000005];
int main(){
int m,n,t;
cin>>m>>t>>n;
int l=1,r=m;
while(l<r){
int mid=(l+r)/2;
cout<<"? "<<1<<" "<<mid<<"\n";
cout.flush();
int x;
cin>>x;
if(mid-x>=n){
r=mid;
}
else{
l=mid+1;
}
}
cout<<"! "<<l;
return 0;
}
G题
题目大意
给定一个n×m的网格,每个格子(i,j)都有一个整数参数 a i j ( − 1 < = a i j < = 1 0 9 ) a_{ij}(-1<=a_{ij}<=10^9) aij(−1<=aij<=109), a i j = − 1 a_{ij}=−1 aij=−1表示这个格子不能通过;否则,这个格子可以通过,若 a i j > 0 a_{ij}>0 aij>0则表示这个格子内有一个传送门。现在小D想要从(1,1)移动到(n,m),它可以以如下两种方式移动:在任意两个相邻且参数均不为−1的格子间移动,花费为w,在有传送门的两个格子 (i,j)和(x,y) 间移动,花费为 a i j + a x y a_{ij}+a_{xy} aij+axy求小D所需的最小花费。
正解思路
分类思考,一是不走传送门,二是走一次传送门,进行两次bfs,求出最终结果。
正解代码
#include <bits/stdc++.h>
using namespace std;
int n,m,w;
int a[2010][2010],dis[2010][2010];
long long ans1=0x3f3f3f3f3f3f3f3f,ans2=0x3f3f3f3f3f3f3f3f,allans=0x3f3f3f3f3f3f3f3f;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void bfs(int sx,int sy,long long &ans){
memset(dis,-1,sizeof(dis));
queue<pair<int,int> >que;
dis[sx][sy]=0;
que.push(make_pair(sx,sy));
while(!que.empty()){
int x=que.front().first,y=que.front().second;
que.pop();
for(int i=0;i<4;i++){
int nx=x+dx[i],ny=y+dy[i];
if(nx<1||ny<1||nx>n||ny>m||a[nx][ny]==-1||dis[nx][ny]!=-1){
continue;
}
dis[nx][ny]=dis[x][y]+1;
que.push(make_pair(nx,ny));
}
}
if(sx==1&&dis[n][m]!=-1){
allans=min(allans,(long long)dis[n][m]*w);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(dis[i][j]!=-1&&a[i][j]>0){
ans=min(ans,(long long)dis[i][j]*w+a[i][j]);
}
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&w);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
}
}
bfs(1,1,ans1);
bfs(n,m,ans2);
allans=min(allans,ans1+ans2);
printf("%lld",allans==0x3f3f3f3f3f3f3f3f?-1:allans);
return 0;
}