2020 ICPC Universidad Nacional 题解


题目链接 2020 ICPC Universidad Nacional de Colombia Programming Contest

B - Baby name [暴力]

题意

给两个串 a , b a, b a,b
可以对 a a a 取出一段连续的子串, b b b 也取出一段连续的子串
使得最后得到的组合串字典序最大( a a a的子串在 b b b之前,且两个长度都不能为 0 0 0

分析

对于每个 a , b a,b a,b 串,直接找到每个最大字母开始的位置
然后比较每个串每个位置,取更大的

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

char s1[maxn], s2[maxn];
vector<int> v1, v2;

void solve(char *s, int len, vector<int> v) {
    int id = 0;
    v.push_back(0);
    for(int i = 1; i < len; ++i) {
        if(s[i] > s[id]) {
            id = i, v.clear();
            v.push_back(i);
        }else if(s[i] == s[id]) {
            v.push_back(i);
        }
        while(i < len && s[i] == s[i+1]) ++i;
    }
    int res = 0;
    for(auto i : v) {
        int j = i, id = res;
        while(j < len && s[id] == s[j]) id++, j++;
        if(j < len && s[id] < s[j]) res = i;
    }
    return res;
}

int main() {
    scanf("%s%s", s1, s2);
    int l1 = strlen(s1), l2 = strlen(s2);
    int ps1 = solve(s1, l1, v1), ps2 = solve(s2, l2, v2);
    putchar(s[ps1++]);
    while(ps1 < l1 && s1[ps1] >= s2[ps2]) putchar(s1[ps1++]);
    while(ps2 < l2) putchar(s2[ps2++]);
    puts("");
    return 0;
}


E - Enter to the best problem of this contest! [思维][交互]

题意

这是一道交互题
现在有一棵深度为 n n n 的完全二叉树,每次要找出当前问题给定的一个点
可以询问不超过 n n n 次,每次询问一个节点编号,然后题目会返回一个数表示定的这个点距离你给的这个点的最短距离

分析

显然是以深度为主要询问的点
那么就询问当前点的左节点,若距离变小了,说明点在左节点子树下
若距离变大了,说明应该在当前节点的右节点子树下。这里需要注意的是若距离变大了,则应该将当前距离-2再赋值给记录前一次距离的变量

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5+100;
const long long mod = 1e9+7;

int n,m;

int main(){
    scanf("%d",&n);
    int dep = n, x = 1, dis = 0, dis1 = 0;
    printf("%d\n",x);
    fflush(stdout);
    scanf("%d",&dis);
    if(dis==0) printf("! %d\n",x);
    else{
        do{
            int t = log2(x)+1;
            if(dep > t)
                x <<= 1;
            printf("%d\n",x);
            fflush(stdout);
            scanf("%d",&dis1);
            if(dis1>dis){
                x |= 1;
                dis = dis1-2;
            }
            else dis = dis1;
        }while(dis!=0);
        printf("! %d\n",x);
    }
}  


G - Great dinner [排列][逆元]

题意

总共有 n n n 个数字,编号为 1 − n 1-n 1n
m m m 个限制,限制是 x x x 必须排在 y y y 后面
问有多少种排列方式

分析

不看限制的排列方式为 n ! n! n!
对于每一个限制,就是除以一个 2 2 2
即结果为 n ! 2 m = n ! × i n v ( 2 m ) \frac{n!}{2^m} = n! \times inv(2^m) 2mn!=n!×inv(2m)

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5+100;
const long long mod = 1e9+7;

long long n,m;

long long qpow(long long a, long long b){
    long long res = 1;
    while(b){
        if(b&1) res = a*res%mod;
        a = a*a%mod;
        b>>=1;
    }
    return res;
}

int main(){
    cin>>n>>m;
    for(int i=1; i<=m; i++){
        int a,b;
        cin>>a>>b;
    }
    long long ans = 1;
    for(int i=n; i>=1; --i){
        ans = ans*i%mod;
    }
    long long res = qpow(2ll,m);
    ans = ans * qpow(res, mod-2) % mod;
    cout<<ans<<endl;
}


K - Katastrophic sort

题意

给两个串,每个串是由两部分组成,前一部分字符串,后一部分数字
问哪个串比较大
字符串部分按照字典序比较,数字部分按照数字大小去比较,比如 11 > 2 11 > 2 11>2

分析

将每个串分别截取其字母和数字部分,先比较字母再比较数字

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;

char s1[maxn], s2[maxn];
char s11[maxn], s22[maxn];

int main() {
    scanf("%s%s", s1, s2);
    int n = strlen(s1), m = strlen(s2);
    int n1=0, m1=0;
    while(s1[n1] >='a' && s1[n1]<='z') s11[n1] = s1[n1], n1++;
    while(s2[m1] >='a' && s2[m1]<='z') s22[m1] = s2[m1], m1++;
    s11[n1] = 0, s22[m1] = 0;
    int k = strcmp(s11, s22);
    if(k) {
        puts(k > 0 ? ">" : "<");
    }else{
        if(n - n1 + 1 < m - m1 + 1) {
            k = -1;
        }else if(n - n1 + 1 > m - m1 + 1) {
            k = 1;
        }else{
            while(n1 < n && m1 < m) {
                if(s1[n1] == s2[m1]) {
                    n1++,m1++;
                    continue;
                }
                else if(s1[n1] < s2[m1]) k = -1;
                else k=1;
                break;
            }
        }
        if(k == 1) puts(">");
        else if(k == 0) puts("=");
        else puts("<");
    }
    return 0;
}
/*
asgfsd4213456
asgfsd421345
*/


L - Lonely day [bfs]

题意

S S S 走到 E E E,每次可以走上下左右其中一个方向的第一个不为 X X X 的地方,求路径最短且字典序最小

分析

b f s bfs bfs 求路径最短,字典序最小则用方向来限制
就是输出的时候要用递归输出,直接每个点记录路径会超时

代码

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const ll mod = 1e9 + 7;
const int maxn = 2e3 + 5;

struct node{
    int x, y, s;
    node(int x=0, int y=0, int s=0) : x(x), y(y), s(s) {}
};
// D L R U
int to[4][2] = {1, 0, 0, -1, 0, 1, -1, 0};
int dir[4] = {'D', 'L', 'R', 'U'};
char mp[maxn][maxn]; // 原图
bool vis[maxn][maxn]; // 是否走过
int pdir[maxn][maxn]; // 方向
int pdis[maxn][maxn]; // 距离
int n, m;

bool check(int xx, int yy) {
    if(xx < 1 || yy < 1 || xx > n || yy > m) return false;
    return true;
}

int bfs(int sx, int sy) {
    queue<node> q;
    while(!q.empty()) q.pop();
    q.push(node(sx, sy)); vis[sx][sy] = true;
    while(!q.empty()) {
        node u = q.front(); q.pop();
        if(mp[u.x][u.y] == 'E') return u.s;
        for(int i = 0; i < 4; ++i) {
            int xx = u.x + to[i][0], yy = u.y + to[i][1];
            while(check(xx, yy) && mp[xx][yy] == 'X') {
                xx += to[i][0], yy += to[i][1];
            }
            if(!check(xx, yy) || mp[xx][yy] == 'Y' || vis[xx][yy]) continue;
            vis[xx][yy] = true, pdir[xx][yy] = i, pdis[xx][yy] = u.y-yy+u.x-xx;
            q.push(node(xx, yy, u.s+1));
        }
    }
    return -1;
}

void print(int sx, int sy, int x, int y) {
    if(x == sx && y == sy) return ;
    int d = pdir[x][y];
    print(sx, sy, x+pdis[x][y]*abs(to[d][0]), y+pdis[x][y]*abs(to[d][1]));
    printf("%c", dir[pdir[x][y]]);
}

int main(){
    int sx, sy, tx, ty;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) {
        scanf("%s", mp[i] + 1);
        for(int j = 1; j <= m; ++j) {
            if(mp[i][j] == 'S') sx=i,sy=j;
            else if(mp[i][j] == 'E') tx=i,ty=j;
        }
    }
    memset(vis, false, sizeof(vis));
    int step = bfs(sx, sy);
    printf("%d\n", step);
    if(step > 0) {
        print(sx, sy, tx, ty);
    }
    return 0;
}


M - Magic spells [二分]

题意

给一个串 s s s。再给出 n n n 个串 t i t_i ti
s s s 串中找出以 t i t_i ti 作为前缀的子序列(可不连续但顺序不变)的最长匹配前缀

分析

将每个字母的位置放到对应数组中
然后枚举每个 t i t_i ti 串,二分找符合位置的第一个下标

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;

vector<int> pos[30];
char s[maxn], t[maxn];

int main() {
    int n;
    scanf("%s%d", s, &n);
    int ls = strlen(s);
    for(int i = 0; i < ls; ++i) {
        pos[s[i]-'a'].push_back(i);
    }
    for(int i = 0; i < n; ++i) {
        scanf("%s", t);
        int pre = -1, len = 0, lt = strlen(t);
        for(int j = 0; j < lt; ++j) {
            int k = t[j]-'a';
            if((int)pos[k].size() == 0) break;
            int kk = upper_bound(pos[k].begin(), pos[k].end(), pre) - pos[k].begin();
            if(kk < 0 || kk >= (int)pos[k].size()) break;
            if(pos[k][kk] < pre)
                break;
            pre = pos[k][kk];
            len = j+1;
        }
        if(len == 0) puts("IMPOSSIBLE");
        else {
            t[len] = '\0';
            puts(t);
        }
    }
    return 0;
}


题意

分析

代码




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值