2019, XII Samara Regional Intercollegiate Programming Contest 解题报告

2019, XII Samara Regional Intercollegiate Programming Contest传送门A. Rooms and Passages题意:有 n+1n+1n+1 个房间,nnn 把钥匙,有第 iii 把钥匙你可以从房间 i−1i-1i−1 走到房间 iii。每个房间 iii 都有两种类型中的一种:一、只有拥有钥匙 aia_iai​ 才能从 i−1...
摘要由CSDN通过智能技术生成

2019 XII Samara Regional Intercollegiate Programming Contest

传送门


A. Rooms and Passages

题意:

n + 1 n+1 n+1 个房间, n n n 把钥匙,有第 i i i 把钥匙你可以从房间 i − 1 i-1 i1 走到房间 i i i。每个房间 i i i 都有两种类型中的一种:

  • 一、只有拥有钥匙 a i a_i ai 才能从 i − 1 i-1 i1 走到 i i i
  • 二、从 i − 1 i-1 i1 走到 i i i 之后会失去钥匙 a i a_i ai

现在问你从每个房间出发开始往后走,最远能走几个房间。

题解:

尺取(two pointers),根据走到房间的种类:如果是种类一,则判断所需钥匙数量是否为 1;如果是种类二,则增加当前房间消耗的钥匙数量,并且左端点退出时减少被消耗的钥匙数量。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=500005;
int a[maxn];
int type[maxn];
int tag[maxn];
int ans[maxn];
int main(){
   
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++){
   
        int x;scanf("%d",&x);
        if(x>0) a[i]=x,type[i]=1;
        else a[i]=-x,type[i]=2;
    }
    int ed=1;
    for(int st=1;st<=n;st++){
   
        while(ed<=n){
   
            if(type[ed]==1){
   
                if(tag[a[ed]]) break;
            }else{
   
                tag[a[ed]]++;
            }
            ed++;
        }
        ans[st]=ed-st;
        if(type[st]==2) tag[a[st]]--;
    }
    for(int i=1;i<=n;i++) printf("%d%c",ans[i],(i==n)?'\n':' ');
    return 0;
}

B. Rearrange Columns

题意:

给定一个 2 ∗ l e n 2*len 2len 的点阵,每个点均为 . . . # \# #,问能否将点阵的每一列重排,使得所有的 # \# # 构成一个联通块。

题解:

显然我们可以分析出,每一列只有第一行是 # \# # 的都可以放到一起,形如:

######
......

同理每一列只有第二行是 # \# # 的也都可以放到一起,形如:

......
######

而这两种情况同时出现的情况下,我们只需要存在某一列两行都是 # \# #,我们就可以把他们拼接成如下形式:

#######......
......#######

这样就成功将所有 # \# # 放到了同一个联通块中。

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1005;
char s[2][maxn];
int cnt[4];
int main(){
   
    scanf("%s%s",s[0],s[1]);
    int len=(int)strlen(s[0]);
    for(int i=0;i<len;i++){
   
        if(s[0][i]=='#'&&s[1][i]=='.') cnt[0]++;
        else if(s[0][i]=='#'&&s[1][i]=='#') cnt[1]++;
        else if(s[0][i]=='.'&&s[1][i]=='#') cnt[2]++;
        else if(s[0][i]=='.'&&s[1][i]=='.') cnt[3]++;
    }
    if(cnt[0]&&cnt[2]&&!cnt[1]) printf("NO\n");
    else{
   
        printf("YES\n");
        for(int j=0;j<cnt[0];j++) printf("#");
        for(int j=0;j<cnt[1];j++) printf("#");
        for(int j=0;j<cnt[2];j++) printf(".");
        for(int j=0;j<cnt[3];j++) printf(".");
        printf("\n");
        for(int j=0;j<cnt[0];j++) printf(".");
        for(int j=0;j<cnt[1];j++) printf("#");
        for(int j=0;j<cnt[2];j++) printf("#");
        for(int j=0;j<cnt[3];j++) printf(".");
        printf("\n");
    }
    return 0;
}

C. Jumps on a Circle

题意:

p p p 个点 0 0 0 p − 1 p-1 p1 围成一圈(即对于 1 < i < n 1 < i < n 1<i<n i i i 的左边为 i − 1 i-1 i1 i i i 的右边为 i + 1 i+1 i+1 1 1 1 的左边为 n n n n n n 的右边为 1 1 1),一个人初始站在 0 0 0,第一次跳 1 1 1 步,第二次跳 2 2 2 步,第 i i i 次跳 i i i 步,共跳 n n n 次,问总共有多少个点被跳到过(包括初始站过的点 0 0 0)。

题解:

数据给出的 n n n 大到 1 0 18 10^{18} 1018,当然我们不能全部跳完,所以我们思考会不会在某个地方出现循环节。而这种情况下的循环节要求到达同一个地方之后的所有行动都要一致。找规律可以发现当 p p p 为奇数时,跳跃 p p p 次即可找到循环节,思考原因我们可以发现,跳跃 p p p 次我们总共跳跃的步数是 ( 1 + p ) ∗ p 2 \frac{(1+p)*p}{2} 2(1+p)p ,其中 1 + p 1+p 1+p 为偶数,即 ( 1 + p ) % 2 = 0 (1+p)\%2=0 (1+p)%2=0,则 ( 1 + p ) ∗ p 2 % p = 0 \frac{(1+p)*p}{2}\%p=0 2

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值