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) ajai=ji(i<j),变形得到 a j − j = a i − i ( i < j ) a_j-j=a_i-i(i<j) ajj=aii(i<j)。这样题目实际上就解决了。利用 m a p map map容器记录 a i − i a_i-i aii出现的次数,遍历数组的时候累加之前出现的 a i − i a_i-i aii的次数即可。需要注意的就是统计用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 midl+1res,我们用这个与 k k k比较,判断我们查询的 k k k在哪个区间即可,需要注意的是,如果是在右边,那么我们需要更新 k k k,因为我们在意的是相对位置,而左区间已经有 m i d − l + 1 − r e s mid-l+1-res midl+1res 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;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(18)  评论(0)    收藏  举报