Codeforces Round #719 (Div. 3) 题解
A.Do Not Be Distracted!
-
解题思路
利用 m a p map map容器记录之前出现过的字母,我们只需要遍历字符串判断当前的字符有没有在之前出现过(注意是不连续的出现)。 -
AC代码
/**
*@filename:A_Do_Not_Be_Distracted_
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-05 22:35
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n;
string s;
map<char,int> p;
void solve(){
p.clear();
char pre=s[0];
p[pre]++;
bool flag=false;
for(int i=1;i<n;i++){
if(s[i]!=pre){
if(p.find(s[i])!=p.end()){
flag=true;
break;
}
else{
pre=s[i];
p[s[i]]++;
}
}
}
if(!flag){
cout<<"YES"<<endl;
}
else{
cout<<"NO"<<endl;
}
}
int main(){
cin>>t;
while(t--){
cin>>n;
cin>>s;
solve();
}
return 0;
}
B.Ordinary Numbers
- 解题思路
此题需要我们知道1~n以内的所有普通数,即位数上的数字都相等,那么这种数有什么规律呢? 1 , 2 , 3 , 4 , 5.... 1,2,3,4,5.... 1,2,3,4,5...., 11 , 22 , 33 , 44... 11,22,33,44... 11,22,33,44..., 111 , 222 , 333 , 444 , 555 111,222,333,444,555 111,222,333,444,555。我们发现,实际上只需要起始从 1 1 1构成的相等数开始,再每次加上这个数即可。需要注意的就是进位。 - AC代码
/**
*@filename:B_Ordinary_Numbers
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-05 22:39
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n;
bool check(int x){
string temp=to_string(x);
for(int i=0;i<temp.size()-1;i++){
if(temp[i]!=temp[i+1])return false;
}
return true;
}
void solve(){
int ans=0;
int k=1,temp=1;
for(int i=temp;i<=n;i+=temp){
if(check(i))ans++;
else{
//不行,说明已经进位,我们需要让temp变为当前长度的1。
temp=temp*10+1;
i=0;
}
}
cout<<ans<<endl;
}
int main(){
cin>>t;
while(t--){
cin>>n;
solve();
}
return 0;
}
C. Not Adjacent Matrix
-
解题思路
构造问题,我们是要让相邻单元格的差值不为 1 1 1,所以我们自然能想到用奇数和奇数相邻,偶数和偶数相邻,所以我们可以先从小到大填充所有的奇数,填完奇数之后再从小到大填充完所有的偶数。这种做法除了 n = 2 n=2 n=2不满足情况,其他的均可满足。因为除 n = 2 n=2 n=2之外奇数和偶数相邻的那部分单元格差值不为 1 1 1。 -
AC代码
/**
*@filename:C_Not_Adjacent_Matrix
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-05 22:49
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100 + 5;
const int mod = 1e9+7;
int t,n;
int a[maxn][maxn];
void solve(){
if(n==2)cout<<-1<<endl;
else{
int odd=1,even=2;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(odd>n*n){
a[i][j]=even;
even+=2;
}
else{
a[i][j]=odd;
odd+=2;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}
}
int main(){
cin>>t;
while(t--){
cin>>n;
solve();
}
return 0;
}
D. Same Differences
-
解题思路
这道题其实特别简单,我们需要将给定的公式变形,即 a j − a i = j − i ( i < j ) a_j-a_i=j-i(i<j) aj−ai=j−i(i<j),变形得到 a j − j = a i − i ( i < j ) a_j-j=a_i-i(i<j) aj−j=ai−i(i<j)。这样题目实际上就解决了。利用 m a p map map容器记录 a i − i a_i-i ai−i出现的次数,遍历数组的时候累加之前出现的 a i − i a_i-i ai−i的次数即可。需要注意的就是统计用long long类型,会爆int。 -
AC代码
/**
*@filename:D_Same_Differences
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-05 22:57
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 200000 + 5;
const int mod = 1e9+7;
int t,n,a[maxn];
map<int,int> p;
void solve(){
ll ans=0;
//aj-j=ai-i;
for(int i=1;i<=n;i++){
ans+=p[a[i]-i];
p[a[i]-i]++;
}
cout<<ans<<endl;
}
int main(){
cin>>t;
while(t--){
cin>>n;
p.clear();
for(int i=1;i<=n;i++)cin>>a[i];
solve();
}
return 0;
}
E. Arranging The Sheep
- 解题思路
根据贪心原则,我们总是想往中间靠,那么我们则需要找到最中间的 ∗ * ∗即可,所以我们可以将这些 ∗ * ∗字符的下标存储起来,需要处理的一个细节就是,我们需要将连续的 ∗ * ∗看成是一个点,因为它们转移的消耗是一样的。处理完之后,开始判断选取哪个中点,取最小值即可。 - AC代码
/**
*@filename:E_Arranging_The_Sheep
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-05 23:16
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1000000 + 5;
const int mod = 1e9+7;
int t,n;
string s;
void solve(){
vector<int> pos;
//我们可以认为连续的就是同一个点,这样方便处理。
int idx=1;
for(int i=0;i<n;i++){
if(s[i]=='*')pos.push_back(idx);
else idx++;
}
if(pos.empty()){
//说明为空。
cout<<0<<endl;
return;
}
//选择中间位置进行处理。
int idx1=pos.size()/2,idx2=(pos.size()-1)/2;
ll cnt1=0,cnt2=0;
/* for(int i=0;i<pos.size();i++){
cout<<pos[i]<<" ";
} */
/* cout<<endl; */
for(int i=0;i<pos.size();i++){
cnt1+=abs(pos[idx1]-pos[i]);
cnt2+=abs(pos[idx2]-pos[i]);
/* cout<<cnt1<<" "<<cnt2<<endl; */
}
cout<<min(cnt1,cnt2)<<endl;
}
int main(){
cin>>t;
while(t--){
cin>>n>>s;
solve();
}
return 0;
}
F1. Guess the K-th Zero (Easy version)
- 题目大意
给定一个 01 01 01数组的长度,我们需要利用不超过 20 20 20次的查询来找到第 k k k个 0 0 0的位置。在简单版本中,我们只需要找一个 k k k。 - 解题思路
对于这种问题,我们应该要想到二分查找,这样我们才能保证在不超过 20 20 20次的查询来找到第 k k k个 0 0 0的位置,因为 2 20 = 1048576 > 2 e 5 2^{20}=1048576>2e5 220=1048576>2e5。那么二分的话我们需要维护一个可行区间 [ l , r ] [l,r] [l,r],每次查询 [ l , m i d ] ( m i d = ( l + r ) > > 1 ) [l,mid](mid=(l+r)>>1) [l,mid](mid=(l+r)>>1),而返回的结果 r e s res res是该区间中 1 1 1的个数,那么自然该区间中 0 0 0的个数为 m i d − l + 1 − r e s mid-l+1-res mid−l+1−res,我们用这个与 k k k比较,判断我们查询的 k k k在哪个区间即可,需要注意的是,如果是在右边,那么我们需要更新 k k k,因为我们在意的是相对位置,而左区间已经有 m i d − l + 1 − r e s mid-l+1-res mid−l+1−res个 0 0 0了,那么终止的时候也就是找到了第 k k k个 0 0 0的位置,即 l = r l=r l=r。 - AC代码
/**
*@filename:F1_Guess_the_K_th_Zero_Easy_version_
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-06 00:06
**/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100000 + 5;
const int mod = 1e9+7;
int t,n,k;
//二分查找。
void solve(){
int T=20;//查找次数最多20次。
int l=1,r=n;
while(T--){
int mid=(l+r)>>1;//我们每次二分查询即可得到所在区间。
cout<<"?"<<" "<<l<<" "<<mid<<endl;
cout.flush();
int res;
cin>>res;//这个即返回的是[l,mid]这段区间的1的数量,那么0的数量自然易得。
int cnt=mid-l+1-res;
if(cnt<k){
//说明第k个0在右边。
k-=cnt;
l=mid+1;
}
else{
//说明第k个0在左边。
r=mid;
}
if(l==r){
cout<<"! "<<l<<endl;
break;
}
}
}
int main(){
cin>>n>>t;
while(t--){
cin>>k;
solve();
}
return 0;
}
F2. Guess the K-th Zero (Hard version)
- 解题思路
目前还不会,待补。
G. To Go Or Not To Go?
-
解题思路
我们知道若 g [ i ] [ ] j ] g[i][]j] g[i][]j]为 − 1 -1 −1,则说明不准通行,否则其他的都可以正常移动,每次移动的消耗为 w w w。特殊的是若该点权值大于 0 0 0,说明可以使用传送器到有传送器的地方,消耗为 g [ i ] [ j ] + g [ u ] [ v ] g[i][j]+g[u][v] g[i][j]+g[u][v]。由于可以任意传送,所以其实使用传送器只会使用一次。那么我们就可以先计算出不使用传送器从 ( 1 , 1 ) (1,1) (1,1)出发和从 ( n , m ) (n,m) (n,m)出发到达各点的最短路径,得到了这些之后,我们就可以枚举使用的传送门(记住,这里的传送门有两处,一处是从起点到达的 ( i , j ) (i,j) (i,j),一处是从 ( i , j ) (i,j) (i,j)传送到达的 ( u , v ) (u,v) (u,v))取最优了,即维护 d i s t 1 [ i ] [ j ] + g [ i ] [ j ] dist1[i][j]+g[i][j] dist1[i][j]+g[i][j]和 d i s t 2 [ i ] [ j ] + g [ i ] [ j ] dist2[i][j]+g[i][j] dist2[i][j]+g[i][j]的最小值,然后再与不使用传送门直接到达 ( n , m ) (n,m) (n,m)的 d i s t 1 [ n ] [ m ] dist1[n][m] dist1[n][m]取最小值即可。 -
AC代码
/**
*@filename:G_To_Go_Or_Not_To_Go_
*@author: pursuit
*@csdn:unique_pursuit
*@email: 2825841950@qq.com
*@created: 2021-05-06 21:45
**/
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int maxn = 2000 + 5;
const int mod = 1e9+7;
const ll inf = 0x3f3f3f3f3f3f3f;
int n,m,w;
ll dist1[maxn][maxn],dist2[maxn][maxn];//dist1[i][j]表示(1,1)到(i,j)的最短路径,dist2[i][j]表示(n,m)到(i,j)的最短路径。
int g[maxn][maxn];//图。
int go[4][2]={0,1,1,0,0,-1,-1,0};//行走路径。
void bfs(pii st,ll dist[maxn][maxn]){
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dist[i][j]=inf;//初始化。
}
}
queue<pii> q;
dist[st.x][st.y]=0;
pii head,temp;
q.push(st);
while(!q.empty()){
head = q.front();
q.pop();
for(int i=0;i<4;i++){
temp.x=head.x+go[i][0],temp.y=head.y+go[i][1];
if(temp.x>=1&&temp.x<=n&&temp.y>=1&&temp.y<=m&&g[temp.x][temp.y]!=-1&&dist[temp.x][temp.y]>dist[head.x][head.y]+w){
dist[temp.x][temp.y]=dist[head.x][head.y]+w;
q.push(temp);
}
}
}
}
void solve(){
bfs({1,1},dist1);
bfs({n,m},dist2);
ll minn1=inf,minn2=inf;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
//穿插使用传送门,优化最小值。
if(g[i][j]<=0)continue;
minn1=min(minn1,dist1[i][j]+g[i][j]);
minn2=min(minn2,dist2[i][j]+g[i][j]);
//cout<<minn1<<" "<<minn2<<endl;
}
}
ll ans=min(minn1+minn2,dist1[n][m]);
if(ans>=inf)ans=-1;
printf("%lld\n",ans);//注意是lld
}
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",&g[i][j]);
}
}
solve();
return 0;
}

浙公网安备 33010602011771号