HUAS 2018暑假第一周比赛-题解

小朋友们有问题评论区 😃

B. 子串计算

难度系数 :
Main idea : 模拟 暴力
按照题目的要求一步一步来就行了
之所以可行的原因是从左往右扫,如果扫到一个子串,把它删除掉之后,假设当前位置的下标是\(i\),新的子串只会出现在\(i-26\)的后面。
时间复杂度\(O(T\cdot |S|)\)

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

int main()
{
    int t;
    scanf("%d", &t);
    while(t--){
        string a, b;
        cin >> a >> b;
        int ans = 0, la = a.size();
        for (int i = 0; i < b.size(); ++i){
            if (i + 1 >= la && b.substr(i + 1 - la, la) == a){
                ++ans;
                b.erase(i + 1 - la, la);
                i -= la;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

C. 栈的游戏

难度系数 :
Main idea : 思维

首先思考一下,什么才是最优策略呢?
假设现在Dum面对的栈从顶到底是11101,他现在有很多种拿法,怎么拿最优呢?此时显然拿一个1是最优的,这样这个栈下次还是只能他拿,那为什么不拿两个1?因为比起拿两个1,拿一个1可以让他”活的更久些!“
假设现在Dum面对的栈从顶到底是10011,他现在拿100是最优的,这样就不会把这个栈的主导权让给Dee,使得这个栈成为永远只被Dum拿的“奴隶!”
通过上面的思考,一个初始时栈顶为1的栈永远只会被Dum拿,且Dum最多可以拿\(Num_1\)次,
\(Num_1\)指的是栈里面1的个数。
同理可得Dee最多可以拿的次数\(Num_0\)
那么如果Dee先开局,只要\(\sum Num_0 > \sum Num_1\),Dee就能赢。
同理其他的情况类似。
时间复杂度\(O(T\cdot n\cdot |S|)\)

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

char name[5], Stack[55];

int main ()
{
    int T, n;
    scanf("%d", &T);
    while (T--) {
        scanf("%d %s", &n, name);
        int num_0 = 0, num_1 = 0;
        for (int i = 1; i <= n; ++i) {
            scanf("%s", Stack);
            int len = strlen(Stack);
            for (int j = 0; j < len; ++j) {
                if (Stack[j] == Stack[0]) {
                    if (Stack[0] == '0') ++num_0;
                    else ++num_1;
                }
            }
        }
        if (name[1] == 'e') puts(num_0 > num_1 ? "Dee" : "Dum");
        else puts(num_1 > num_0 ? "Dum" : "Dee");
    }
    return 0;
}

E. 字符串挑战赛

难度系数 :
Main idea : 模拟 暴力

使用一个bool数组mark[c][num]表示字符c是否连续出现了num次,可以遍历一次字符串预处理出这个数组。
然后对于每一个询问\(m_i\),枚举从\(a\)\(z\)的字符,如果是\(Yes\)的情况,那么必然有一个字符\(x\)满足
\(Val_x\cdot Num_x = m_i\),其中\(Val_x\)表示字符\(x\)的价值,\(Num_x\)表示字符\(x\)连续出现的次数。那么只需要看看\(mark[x][\frac{m_i}{x}]\)是不是为\(true\)就好了。
时间复杂度\(O(26\cdot M + |S|)\)

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

const int N = 100005;

char s[N];
bool mark[30][N];

int main ()
{
    int n, x;
    scanf("%s %d", s + 1, &n);
    int len = strlen(s + 1);
    int num = 0;
    for (int i = 1; i <= len; ++i) {
        if (i != 1 && s[i] != s[i - 1]) num = 1;
        else ++num;
        mark[s[i] - 'a' + 1][num] = true;
    }
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &x);
        bool mark = false;
        for (int j = 1; j <= 26; ++j) {
            if (x % j || x / j > len || mark[j][x / j] == false) continue;
            mark = true;
        }
        puts(mark ? "Yes" : "No");
    }
    return 0;
}

F. 较小元素

难度系数 :
Main idea : STL set

有不下十几种方法解决这个问题,介绍一个最容易写的。
从前往后遍历数组,维护一个\(set\),对于数组的当前元素\(a_{current}\),使用\(set\)\(lower\_bound\)函数查找\(set\)里面满足\(\ge a_{current}\)的元素里的最小元素,再把\(a_{current}\)插入\(set\)里面就行了。
但是题目问的是当前元素前面的元素里满足\(<a_{current}\)的元素里的最大元素。
怎么办?
首先把数组里的每个元素取个相反数就行了。

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

const int N = 100005;

set<long long> s;

int main ()
{
    int n;
    long long x;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%lld", &x);
        x = -x;
        auto iter = s.lower_bound(x + 1);
        if (iter == s.end()) puts("-1");
        else printf("%lld\n", -*iter);
        s.insert(x);
    }
    return 0;
}

D. IPC训练者

难度系数 : ★★
Main idea : 贪心 STL 优先队列
我们从第一天依次考虑到最后一天,那么显然每一天应该要让当前能用的教练中扎心值最大的教练上课,所以需要一个数据结构支持查询最大值,插入元素,删除最大值。显然\(STL\)里的\(priority\_queue\)可以完美实现这些要求。
时间复杂度\(O(T\cdot M\cdot log(N))\)

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

const int N = 100005;

struct Node{
    int beg, day, s;
}node[N];
struct cmp{
    bool operator () (Node a, Node b) {
        return a.s < b.s;
    }
};
priority_queue<Node, vector<Node>, cmp> que;

bool comp(Node a, Node b) {
    return a.beg < b.beg;
}
int main ()
{
    int T, n, D, beg, day, s;
    scanf("%d", &T);
    Node tmp;
    while (T--) {
        scanf("%d %d", &n, &D);
        for (int i = 1; i <= n; ++i) scanf("%d %d %d", &node[i].beg, &node[i].day, &node[i].s);
        sort(node + 1, node + n + 1, comp);
        int now = 1;
        for (int i = 1; i <= D; ++i) {
            while (now <= n && node[now].beg == i) que.push(node[now]), ++now;
            if (!que.empty()) {
                tmp = que.top();
                que.pop();
                --tmp.day;
                if (tmp.day) que.push(tmp);
            }
        }
        long long ans = 0;
        while (!que.empty()) ans += (long long)que.top().day * que.top().s, que.pop();
        printf("%lld\n", ans);
    }
    return 0;
}

A. 脆弱核心

难度系数 : ★★★
Main idea :

首先可以很轻易得想到了暴力的做法,就是一天一天的模拟,每天扫描一下数组,把满足条件的元素删掉,直到某一天删不掉任何一个元素为止,这样的时间复杂度为\(O(n^2)\),为什么?因为一天最少删除\(1\)个元素,而最多只能删\(n-2\)个元素,所以最坏的情况下,需要模拟\(n-2\)天。这样是显然过不了\(n=10^5\)的数据的,一般来说,oj时限\(1s\)可以run \(O(10^8)\)
不妨考虑简化一下问题,如果我们只需要求出最后会剩下哪些元素,那么可以用栈来得到一个\(O(n)\)的办法,伪代码如下:
从左到右扫描数组,
1.如果现在栈里面的元素个数\(\le1\),那么把当前元素压入栈。
2.设当前栈顶元素为\(a[top]\),现在要处理的元素为\(a[i]\)
-----> 1).如果$a[top] < a[top-1] $ 且 $ a[top] < a[i]$,弹出当前栈顶元素,转到2.
-----> 2).否则把当前元素压入栈。
扫描完数组后,栈里面的元素就是最后会剩下的元素了。
为什么? 和B题类似的思路。

理解好上面的思想之后就让我们回到原问题,原问题还需要输出每个元素会在哪天被删掉。
我们上面的办法只能求出不会被删掉的元素,所以我们还需要进一步的思考。
如果你真的理解了上面的伪代码了的话,其实剩下该做的事情也不难了,上面的伪代码不仅告诉了我们
哪些元素会被删掉,还告诉了我们每个元素是被它旁边的哪两个元素给删掉的,不访假设\(a[i]\)是被\(a[l]\)\(a[r]\)给删掉的\((l \le i \le r)\),那么也就是说,\(a[i]\)一定会比\(a[l+1]...a[i-1], a[i+1]...a[r-1]\)晚删掉,多晚呢?必然是这些元素全部被删掉的那一天再加一,为什么?考虑反证法。

时间复杂度\(O(N\cdot T)\)
具体实现的细节看代码吧。

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

const int N = 100005;

int ans[N], a[N], st[N], Max[N], top;

int main ()
{
    int T, n, x;
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) ans[i] = Max[i] = 0;
        top = 0;
        for (int i = 1; i <= n; ++i) scanf("%d", a + i);
        for (int i = 1; i <= n; ++i) {
            while (top >= 2 && a[st[top]] < a[st[top - 1]] && a[st[top]] < a[i]) {
                ans[st[top]] = max(Max[st[top - 1]], Max[st[top]]) + 1;
                Max[st[top - 1]] = ans[st[top]];
                --top;
            }
            st[++top] = i;
        }
        for (int i = 1; i <= n; ++i) printf(i == 1 ? "%d" : " %d", ans[i]);
        puts("");
    }
    return 0;
}
posted @ 2018-07-27 16:45  free-loop  阅读(348)  评论(1编辑  收藏  举报