寒假专题一题解

专题一部分题解

@toc

B - Assistance Required

题目链接

思路 :有点类似埃氏筛, 使用数组标记元素是否需要出列

#include <iostream>
const int N = 4e4;
using namespace std;
int p[N],cnt;
int book[N];
int main() {
    for (int i = 2;i < N;i ++)  {  
        if(!book[i]) {  
            p [cnt ++ ] = i;
            int k = 0;
            for (int j = i + 1; j < N; j ++ ) {
                if (!book[j]) {
                    k ++ ;
                    if (k == i) {
                        k = 0;
                        book[j] = 1;
                    }
                }
            }  
        }  
    }
    int n;
    while (cin >> n && n) {
        cout << p[n - 1] << endl;
    }
    return 0;
}

E - Train Problem I

题目链接

思路 :判断一个栈的出队序列是否合法 并 记录入队和出队的操作

#include <iostream>
#include <stack>
#include <cstring>
using namespace std;
char a[2000], b[2000];
int n;
int record[3000];//in 1 out 0
int main() {
    while (cin >> n >> a >> b) {
        stack<char> t;
        int loc = 0, cnt = 0;
        memset(record, 0, sizeof(record));
        for (int i = 0; i < n; i ++ ) {
            t.push(a[i]);
            record[cnt ++ ] = 1;
            while(!t.empty() && t.top() == b[loc]) {
                t.pop();
                loc ++ ;
                record[cnt ++ ] = 0;
            }
        }
        if (loc == n) {
            cout << "Yes." << endl;
            for (int i = 0; i < cnt; i ++ ) {
                if (record[i]) cout << "in" << endl;
                else cout << "out" << endl;
            }       
        }
        else {
            cout << "No." << endl;
        } 
        cout << "FINISH" << endl;
    }
}

K - Boxes in a Line

题目链接

思路: 双向链表 实现插入 交换 翻转的操作
值得注意的是

  1. 交换操作要特判相邻的情况
  2. 操作4如果将整个链表翻转, 时间复杂度不允许。 使用fx变量标记方向(0正 1反),fx = 1时 操作1改为操作2,操作2改为操作1;此外,最后求奇数项和的时候,fx = 1时,可以用总和减去顺序情况下的奇数项和,即为所求。
#include <iostream>
#include <algorithm>

using namespace std;
const int N = 1e5 + 10;
#define ll long long
int l[N],r[N],fx;
ll ans;
void link(int x, int y) {
    r[x] = y;
    l[y] = x;
}

void init(int n) {
    r[0] = 1;
    l[n + 1] = n;
    for (int i = 1; i <= n; i ++ ) {
        l[i] = i - 1; r[i] = i + 1;
    }
}
void op1(int x, int y) {
    link(l[x],r[x]);link(l[y],x);link(x,y);
}
void op2(int x, int y) {
    link(l[x],r[x]);link(x,r[y]);link(y,x);
}
void op3(int x, int y) {
    int lx = l[x],rx = r[x],ly = l[y],ry = r[y];
    if (y == r[x]) {
        link(lx, y);link(y, x);link(x, ry);
    } else if (x == r[y]) {
        link(ly, x);link(x, y);link(y, rx);
    } else {
        link(lx, y);link(y, rx);link(ly, x);link(x, ry);
    }
}

int main() {
    int L, T;
    int q = 1;
    while (cin >> L >> T) {
        ans = 0;
        fx = 0;
        init(L);
        while (T -- ) {
            int a, b, c;
            cin >> a;
            if (a == 4) fx = !fx;
            else {
                cin >> b >> c;
                if (a == 1) {
                    if(!fx) op1(b, c);
                    else op2(b, c);
                } else if (a == 2) {
                    if(!fx) op2(b, c);
                    else op1(b, c);
                } else if (a == 3) {
                    op3(b, c);
                }
            }
        } 
        int i = 0;
        int cnt = 1;
        for(i = r[i]; i != L + 1; i = r[i]) {
            if(cnt & 1) ans += i;
            cnt ++ ;
        }
        if (fx && L % 2 == 0) {
            ans = (ll) L * (L + 1) / 2 - ans;
        }
        cout << "Case "<< q << ": " << ans << endl;
        q ++ ;
    }
}

L - Random Events

题目链接

思路
对于长度为 n n n的原序列与排序后序列有 k k k个不同的项,显然,当且仅当最后一个不同的项被排序好之后,原序列才合法。于是考虑每一个包含该项在内的操作。若都不能成功排序,无论之前是如何排序的,序列最终都不合法。记这种情况的概率为 p p p,则所求概率为 1 − p 1-p 1p.

#include <iostream>
#include <algorithm>
const int N = 1e6 + 10;
using namespace std;
int a[N], b[N], loc;

int main() {
    int T;
    cin >> T;
    while (T -- ) {
        double ans = 1;
        int n, t;
        cin >> n >> t;
        for (int i = 1; i <= n; i ++ ) {
            cin >> a[i];
            b[i] = a[i];
        }
        sort(b + 1, b + n + 1);
        loc = n;
        while (loc && a[loc] == b[loc]) {
            -- loc;
        }
        while (t -- ) {
            int k; double p;
            cin >> k >> p;
            if (k >= loc) ans *= (1 - p); 
        }
        if (!loc) printf("1.000000\n");
        else printf("%.6lf\n", 1 - ans); 
    }
}

M - Jumps

题目链接

题意:起点为 0 0 0,第 i i i次有两种操作:向前 i i i步或后退 1 1 1步. 问, 最少多少次操作可以到达 X X X.
思路

假设我们每一次操作都选择向前。那么 n n n次操作的最远距离显然是 1 + 2 + . . . + n = n ( n + 1 ) 2 1+2+...+n = \frac{n(n+1)}{2} 1+2+...+n=2n(n+1),考虑其中任意一次操作 k i ( 1 ≤ i ≤ n ) k_i(1\le i\le n) ki(1in)改为后退一步。则所到达的位置变为 1 + 2 + . . . + n − i − 1 = n ( n + 1 ) 2 − i − 1 1+2+...+n-i-1 = \frac{n(n+1)}{2} - i -1 1+2+...+ni1=2n(n+1)i1。 简单来说,这意味着对于某个点 X X X,我们都可以先考虑一直向前的操作。当我们第一次超过 X X X时,即为最少操作数。(因为我们可以通过任意一步后退的做法,使位置向前 d d d个单位),值得注意的是 d d d的范围是 [ 2 , n + 1 ] [2,n + 1] [2,n+1]。这意味着,我们在 k k k次前进操作之后如果到达了 X + 1 X + 1 X+1。我们无法通过上述操作到达 X X X,只能再单独进行一次后退操作,所以最后操作数为 k + 1 。 k +1。 k+1

#include <iostream>
using namespace std;
int main() {
    int T;
    cin >> T;
    while (T -- ) {
        int x; cin >> x;
        int ans = 0;
        while((ans + 1) * ans / 2 < x) ans ++ ;       
        if ((ans + 1) * ans / 2 == x + 1) ans ++ ;
        cout << ans << endl;
    }
}

O - Killjoy

题目链接

思路
1.如果分数都为 x x x,不需要操作
2.如果总分数的平均值为 x x x,只需操作一次
3.如果有至少一个人的分数为 x x x, 那么其他人可以使自己分数变为 x x x将自己感染再变回原分数。需要一次
4.如果无人分数为 x x x,则要先操作一次使得有一个 x x x,再进行第三步。需要两次

P - Saving the City

题目链接

思路:贪心考虑每块空地填雷的价值

核心代码

int flag = 0;
for (int i = 1; i <= len; i ++ ) {
    if (s[i] == '1' ) flag = 1;
    if (s[i] == '0') {
        if (flag) cnt ++ ; 
    } else if (cnt) {  
        ans += (cnt * b < a) ? cnt * b : a;
        cnt = 0;  
    }
}
cout << ( flag ? ans + a : ans ) << endl;

训练赛部分题解

A - Wizard of Orz

题目链接

思路
关键在于第三位是9而不是7,第一次wa,想成了987… 实际上选择在第二位停可以变成98901234567890123…

C - String Equality

题目链接

思路:因为存在交换字母的操作,所以不需要考虑字母本身的位置,只需要考虑字母的出现次数,从a至z遍历,假设该字母在串一中出现 m m m次,在串二中出现 n n n次。有三种情况
1. m < n m< n m<n, 操作2无法完全修改串一, 不行
2. m = n m = n m=n,操作2可以修改串一
3. m > n m> n m>n, 记 d = m − n d = m - n d=mn,若 d 能 整 除 k d能整除k dk,则可以通过操作2将该字母改成下一个字母, 即 让 m i + 1 + d 让m_{i+1}+d mi+1+d

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值