2019 浙江省大学生程序设计竞赛

2019 浙江省大学生程序设计竞赛

在这里插入图片描述

E - Sequence in the Pocket

题意:给出一个整数序列,每次可以任意选择一个元素移动到开头,最少需要多少次操作使得序列非递减;

思路:找到从最大值开始依次倒序排序的最长子序列的长度cnt即可,该子序列不需要额外排序,把剩下的每个元素按顺序提到开头即可,答案为n-cnt;

AC code:

void solve() {
    int n; cin >> n;
    int mx = 0, pos = -1;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        b[i] = a[i];
        if (a[i] >= mx) {
            mx = a[i];
            pos = i;
        } 
    }
    sort(b + 1, b + n + 1);
    int st = n;
    int cnt = 0, now = mx;
    for (int i = pos; i >= 1; i --) {
        if (a[i] == now) {
            cnt ++;
            st -= 1;
            now = b[st];
        }
    }
    if (cnt == n) {
        cout << 0 << endl;
        return;
    }
    cout << n - cnt << endl;
}

F - Abbreviation

题意:给出元音字母,将一个字符串中的元音字母全部删除,注意如果元音字母为首字母则不删除;

思路:按题意模拟删除即可;

AC code:

map<char, int> mp;

void solve() {
    string s; cin >> s;
    string ans = "";
    ans += s[0];
    for (int i = 1; i < s.size(); i ++) {
        if (!mp[s[i]]) ans += s[i];
    }
    cout << ans << endl;
}

signed main() {
    fast();
    
    int T;
    T = 1;
    cin >> T;
    mp['a'] ++;
    mp['e'] ++;
    mp['i'] ++;
    mp['y'] ++;
    mp['o'] ++;
    mp['u'] ++;
    while (T --) {
        solve();
    }
    return 0;
}

G - Lucky 7 in the Pocket

题意:找出一个最小的幸运数m>=n,m满足能被7整除且不能被4整除;

思路:暴力枚举;

AC code:

void solve() {
    int n; cin >> n;
    for (int i = n; ; i ++) {
        if (i % 7 == 0 && i % 4 != 0) {
            cout << i << endl;
            return;
        }
    }
}

H - Singing Everywhere

题意:整数序列中的极大值点为减分点,可以删除序列中的一个整数,减分点最少可以有多少个;

思路:暴力枚举删除每个点的情况,是否会令两边增加/减少减分点即可;

AC code:

void solve() {
    int n; cin >> n;
    for (int i = 1; i <= n; i ++) cin >> a[i];
    int cnt = 0;
    unordered_map<int, int> pos;
    for (int i = 2; i < n; i ++) {
        if (a[i] > a[i - 1] && a[i] > a[i + 1]) {
            cnt ++;
            pos[i] ++;
        }
    }
    int mx = 0;
    for (int i = 1; i <= n; i ++) {
        int ca = 0;
        bool flag = false;
        if (i - 2 >= 1 && a[i - 1] > a[i - 2] && a[i - 1] > a[i + 1]) {
            if (!pos[i - 1]) ca --;
        } else {
            if (pos[i - 1]) ca ++;
        }
        if (i + 2 <= n && a[i + 1] > a[i + 2] && a[i + 1] > a[i - 1]) {
            if (!pos[i + 1]) ca --;
        } else {
            if (pos[i + 1]) ca ++;
        }
        mx = max(ca, mx);
    }
    //cout << cnt << ' ' << mx << "+++" <<endl;
    cout << cnt - mx << endl;
}

I - Fibonacci in the Pocket

题意:给出整数l和r,判断l到r的斐波那契数列的奇偶;

思路:数据为天文数字,所以不能直接去判断,通过打表找规律可以看出斐波那契的奇偶每三个为一组变换,由此得出我们只需要去统计两边界l和r的位和,再分别进行模3处理判断奇偶性;

AC code:

void solve() {
    string a, b; cin >> a >> b;
    int cnta = -1, cntb = 0;
    for (auto c : a) cnta += c - '0';
    for (auto c : b) cntb += c - '0';
    if (cnta % 3 == 1 && cntb % 3 != 1) {
        cout << 1 << endl;
    } else if (cnta % 3 != 1 && cntb % 3 == 1) {
        cout << 1 << endl;
    } else {
        cout << 0 << endl;
    }
}

J - Welcome Party

题意:有n个人参加展会,其中m组人两两互相为朋友,当一个人进入展会时,如果有朋友在展会内,则他会高兴,给出n个人依次进入展会最少能有多少个人不高兴,如果有多种情况,给出最小字典序进入序列;

思路:

首先并查集存取有朋友关系的人的联通块,在该联通块中可以通过人一个人先进入,来换取同一联通块内所有人高兴;

统计联通块的数量即为最少的不高兴的人数;

然后将每个联通块的祖宗节点放入小跟堆优先队列中,队列中的人为当前可以随时进入的人,每次取出最小保证字典序最小;

每次取出时再将当前节点的子节点,即当前人的朋友压入队列中;

最后得到的即为最少不高兴人数的最小入场字典序。

AC code:

int n, m;
int cnt[N], f[N];
vector<int> g[N];

int find(int x) {
    if (f[x] != x) f[x] = find(f[x]);
    return f[x];
}

void solve() {
    cin >> n >> m;
    for (int i = 1; i <= n; i ++) {
        f[i] = i;
        cnt[i] = 0;
        g[i].clear();
    }
    while (m --) {
        int u, v; cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);

        int x = find(v), y = find(u);
        if (x == y) continue;
        if (x < y) f[y] = x;
        else f[x] = y;
    }
    vector<int> st(n + 1, 0);
    vector<int> ans;
    int sum = 0;
    priority_queue<int, vector<int>, greater<int>> q;
    for (int i = 1; i <= n; i ++) {
        if (f[i] == i) q.push(i), st[i] = 1;
    }
    cout << q.size() << endl;
    while (!q.empty()) {
        auto t = q.top();
        q.pop();
        ans.push_back(t);
        for (auto x : g[t]) {
            if (!st[x]) {
                st[x] = 1;
                q.push(x);
            }
        }
    }
    cout << ans[0];
    for (int i = 1; i < ans.size(); i ++) cout << ' ' << ans[i];
    cout << endl;
}

K - Strings in the Pocket

题意:给出字符串s和字符串t,通过一次操作翻转字符串s的任意连续子区间,可以得到字符串t,这样的操作有多少种;

思路:

字符串s与字符串t不一样:

  • 需变区间一定能通过一次翻转来变成字符串t,如果不能,则答案为0;
  • 如果能,向该需变区间的两端拓,拓展字符若为回文则可行的操作+1,否则break;

字符串s与字符串t一样:

  • 直接统计字符串s中的回文字符串的个数即可;
  • 这里用到manacher算法来On统计出字符串s的回文子串数(板子,直接用);

AC code:

#include<bits/stdc++.h>
#define endl '\n'
#define int long long
#define fast() ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr)
using namespace std;

typedef long long LL;
const int N = 4e6+10, M = 2001;
int len;
string s, t;
int Len[N];
char str[N];

int manacher() {
    int mx = 0, id;
    int mxx = 0;
    for (int i = 1; i < len; i ++) {
        if (mx > i) Len[i] = min(mx - i, Len[2 * id - i]);
        else Len[i] = 1;
        while (str[i + Len[i]] == str[i - Len[i]]) Len[i] ++;
        if (Len[i] + i > mx) {
            mx = Len[i] + i;
            id = i;
            mxx = max(mxx, Len[i]);
        }
    }
    int ans = 0;
    for (int i = 0; i < len; i ++) {
        ans += Len[i] / 2;
    }
    return ans;
}

void change() {
    int k = 0;
    str[k ++] = '@';
    for (int i = 0; i < len; i ++) {
        str[k ++] = '#';
        str[k ++] = s[i];
    }
    str[k ++] = '#';
    len = k;
    str[k] = 0;
}

void solve() {
    cin >> s >> t;
    len = s.size();
    int L = -1, R = -1;
    for (int i = 0; i < len; i ++) {
        if (s[i] != t[i]) {
            if (L == -1) L = i;
            R = i;
        }
    }
    int ans = 0;
    if (L != -1) {
        int l = L, r = R;
        while (l <= R) {
            if (s[l] != t[r]) {
                cout << 0 << endl;
                return;
            }
            l ++, r --;
        }
        l = L - 1, r = R + 1;
        ans = 1;
        while (l >= 0 && r < len) {
            if (s[l] == s[r]) ans ++;
            else break;
            l --, r ++;
        }
        cout << ans << endl;
    } else {
        change();
        cout << manacher() << endl;
    }
}

signed main() {
    fast();
    
    int T = 1;
    cin >> T;
    while (T --) {
        solve();
    }
    return 0;
}
 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值