Codeforces Round #501 (Div. 3)

链接:https://codeforces.com/contest/1015
A - Points in Segments
题意:给定n个范围(l_i,r_i),问哪些点没有出现过
思路:由于(1leq l_i leq r_i leq m)且m的范围很小,模拟即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;

const int N = 110;

int n, m, vis[N];
vector<int> res;

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        int l, r;
        scanf("%d%d", &l, &r);
        for (int k = l; k <= r; k++) vis[k] = 1;
    }
    for (int i = 1; i <= m; i++) {
        if (vis[i]) continue;
        res.push_back(i);
    }
    printf("%d
", res.size());
    for (int i = 0; i < res.size(); i++) {
        printf("%d", res[i]);
        printf(i == res.size() - 1 ? "
" : " ");
    }
    return 0;
}

B - Obtaining the String
题意:给定两个字符串,每次可以交换相邻两个字符,给出任意一组交换次数小于(10^4)的方案使得a串成为b串,输出交换的次数与位置,无解输出-1
思路:从后向前处理b串,每次找到一个位置p使得a[p]=b[i],然后模拟交换的过程

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;

const int N = 60;

int n, c[N];
char a[N], b[N];
vector<int> res;

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d%s%s", &n, a + 1, b + 1);
    for (int i = 1; i <= n; i++) c[a[i] - 'a' + 1] += 1;
    for (int i = 1; i <= n; i++) c[b[i] - 'a' + 1] -= 1;
    for (int i = 1; i <= 26; i++) {
        if (0 != c[i]) {
            printf("-1
");
            return 0;
        }
    }
    for (int i = n; i >= 1; i--) {
        if (a[i] == b[i]) continue;
        int p = 0;
        for (int k = 1; k <= i - 1; k++)
            if (a[k] == b[i]) p = k;
        for (int k = p; k <= i - 1; k++) {
            res.push_back(k);
            swap(a[k], a[k + 1]);
        }
    }
    printf("%d
", res.size());
    for (int i = 0; i < res.size(); i++) {
        printf("%d", res[i]);
        printf(i == res.size() - 1 ? "
" : " ");
    }
    return 0;
}

C - Songs Compression
题意:有m大小的空间,有n个物品,第i个物品本来的大小为(a_i),压缩后的大小为(b_i),问你最少只需要压缩多少个物品,就能把这个n个物品放进m大小的空间
思路:贪心,对于每个物品,按照(a_i-b_i)从大到小排序,然后找到最前面最少的物品压缩,使得这n个物品放进m大小的空间

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;

const int N = 100010;

struct node {
    ll a, b, d;
};

int n;
ll m;
node nd[N];

bool cmp(node a, node b)
{
    return a.d > b.d;
}

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d%lld", &n, &m);
    ll sa = 0, sb = 0;
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld", &nd[i].a, &nd[i].b);
        sa += nd[i].a;
        sb += nd[i].b;
        nd[i].d = nd[i].a - nd[i].b;
    }
    if (sb > m) {
        printf("-1
");
        return 0;
    }
    sort(nd + 1, nd + n + 1, cmp);
    int res = 0;
    for (int i = 1; i <= n; i++) {
        if (sa <= m) break;
        sa -= nd[i].d;
        res += 1;
    }
    printf("%d
", res);
    return 0;
}

D - Walking Between Houses
题意:现在有n个房子排成一列,编号为(1~n),起初你在第1个房子里,现在你要进行k次移动,每次移动一都可以从一个房子i移动到另外一个其他的房子j里(i!=j),移动的距离为(mid j-imid)。问你进过k次移动后,移动的总和可以刚好是s,若可以则输出YES并依次输出每次到达的房子的编号,否则输出NO
思路:由于每次最多移动(n-1)步,最少移动一步,所以当(s<k)或者(s>k*(n-1))时,无解,直接输出NO即可,有解情况下,考虑以下构造方法
对于每一步,平均每次需要移动(frac{s}{k})步,其中有(s\%k)次需要移动(frac{s}{k}+1)步,所以采用向左走一次、向右走一次的方法即可

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;

ll n, k, s;
vector<ll> res;

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%lld%lld%lld", &n, &k, &s);
    if (s < k || s > k * (n - 1)) {
        printf("NO
");
        return 0;
    }
    printf("YES
");
    ll b = s / k, bm = s % k, now = 1, t = 1;
    for (ll i = 1; i <= bm; i++) {
        if (1 == now) res.push_back(now + b + 1);
        else res.push_back(now - b - 1);
        now = now + (b + 1) * t;
        t = -t;
    }
    for (ll i = bm + 1; i <= k; i++) {
        if (now + b <= n) {
            res.push_back(now + b);
            now += b;
        }
        else {
            res.push_back(now - b);
            now -= b;
        }
    }
    for (ll i = 1; i <= k; i++) {
        printf("%lld", res[i - 1]);
        printf(i == k ? "
" : " ");
    }
    return 0;
}

E - Stars Drawing (Easy Edition)
见Hard Edition
F - Stars Drawing (Hard Edition)
题意:给你一个(n*m)大小的字符矩阵,仅由"."和"*"组成,提问这个图可否划分为一些由"*"组成的十字形状,这些十字之间可以有重叠,如果可以完全覆盖输出每个十字中心坐标与边长度,不可以输出-1
思路:对于每个"*",我们预处理出他上下左右能延伸的最大范围,对于每个"*",我们就能知道他能够形成以他为中心的十字形状最大为多少,然后差分维护一下哪些点被选过了,如果到最后还有点没有被选到,那就是不可能的,输出-1

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std;

typedef long long ll;

const int N = 1010;

struct node {
    int a, b, r;
    node() { }
    node(int _a, int _b, int _r) : a(_a), b(_b), r(_r) { }
};

int n, m, pre[N][N], af[N][N], up[N][N], down[N][N];
int bc[N][N], br[N][N];
char s[N][N];
vector<node> res;

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%s", s[i] + 1);
    for (int i = 1; i <= n; i++) {
        int now = 0;
        for (int k = 1; k <= m; k++) {
            if (s[i][k] == '.') pre[i][k] = now = 0;
            else {
                pre[i][k] = now;
                if (0 == now) now = k;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        int now = 0;
        for (int k = m; k >= 1; k--) {
            if (s[i][k] == '.') af[i][k] = now = 0;
            else {
                af[i][k] = now;
                if (0 == now) now = k;
            }
        }
    }
    for (int k = 1; k <= m; k++) {
        int now = 0;
        for (int i = 1; i <= n; i++) {
            if (s[i][k] == '.') up[i][k] = now = 0;
            else {
                up[i][k] = now;
                if (0 == now) now = i;
            }
        }
    }
    for (int k = 1; k <= m; k++) {
        int now = 0;
        for (int i = n; i >= 1; i--) {
            if (s[i][k] == '.') down[i][k] = now = 0;
            else {
                down[i][k] = now;
                if (0 == now) now = i;
            }
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int k = 1; k <= m; k++) {
            if ('.' == s[i][k]) continue;
            int imin = 0;
            if (0 != pre[i][k] && 0 != af[i][k] && 0 != up[i][k] && 0 != down[i][k]) {
                imin = min(k - pre[i][k], af[i][k] - k);
                imin = min(imin, i - up[i][k]);
                imin = min(imin, down[i][k] - i);
            }
            if (0 == imin) continue;
            res.push_back(node(i, k, imin));
            bc[i][k - imin] += 1;
            bc[i][k + imin + 1] -= 1;
            br[i - imin][k] += 1;
            br[i + imin + 1][k] -= 1;
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int k = 1; k <= m; k++) {
            bc[i][k] += bc[i][k - 1];
            br[i][k] += br[i - 1][k];
        }
    }
    int ok = 1;
    for (int i = 1; i <= n; i++) {
        for (int k = 1; k <= m; k++) {
            if ('.' == s[i][k]) continue;
            if (0 == bc[i][k] && 0 == br[i][k]) ok = 0;
        }
    }
    if (0 == ok) {
        printf("-1
");
        return 0;
    }
    printf("%d
", res.size());
    for (int i = 0; i < res.size(); i++) printf("%d %d %d
", res[i].a, res[i].b, res[i].r);
    return 0;
}

G - Bracket Substring
题意:给定一个只由左右括号组成的、长度为m的字符串s,问长度为(2*n)的包含s的合法括号序列方案数,答案对1000000007取模,(1leq nleq 100,1leq |s|leq 200)
思路:字符串下标从1开始,to[i][0/1]表示对于[1,i]这个前缀,在后面加上一个"("或者")"后,这个字符串的后缀与s前缀能够匹配的最大长度为多少,可以暴力处理
假设dp[i][j][k][p]表示前i个字符,有j个"("还没有被匹配掉,同时这个i个字符的后缀能够与s匹配的最大长度为k,并且是否完全包含s,所以枚举前i个字符的状态,对于第i+1个字符
如果第i+1个字符为"(",那么这个i+1个字符能够与s匹配的最大长度就会变成to[k][0](相当于[1,k]这个前缀加上"(",即to[k][0]),注意此时to[k][0]可能等于m,那么dp[i+1][j+1][to[k][0]][p|(to[k][0] == m)]需要加上dp[i][j][k][p]
如果第i+1个字符为")",同样dp[i+1][j-1][to[k][1]][p|(to[k][1] == m)]需要加上dp[i][j][k][p]
最后的答案就是(sum_{i=0}^{m} dp[2*n][0][i][1])

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 210;
const int mod = 1000000007;

int dp[N][N][N][2], to[N][2], n, m, cnt;
char a[N], b[N];

bool check(int la, int lb, int len)
{
    for (int i = 0; i < len; i++)
        if (a[la + i] != b[lb + i]) return false;
    return true;
}

int calc(char *b, int len)
{
    for (int i = len; i >= 1; i--)
        if (check(1, len - i + 1, i)) return i;
    return 0;
}

int main()
{
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    scanf("%d%s", &n, a + 1);
    m = strlen(a + 1);
    if (a[1] == '(') to[0][0] = 1;
    else to[0][1] = 1;
    for (int i = 1; i <= m; i++) {
        b[++cnt] = a[i];
        b[++cnt] = '(';
        to[i][0] = calc(b, cnt);
        b[cnt] = ')';
        to[i][1] = calc(b, cnt);
        cnt -= 1;
    }
    dp[0][0][0][0] = 1;
    for (int i = 0; i <= 2 * n - 1; i++) {
        for (int j = 0; j <= n; j++) {
            for (int k = 0; k <= m; k++) {
                for (int p = 0; p <= 1; p++) {
                    if (j + 1 <= n) {
                        int &now = dp[i + 1][j + 1][to[k][0]][p | (to[k][0] == m)];
                        now = (now + dp[i][j][k][p]) % mod;
                    }
                    if (j >= 1) {
                        int &now = dp[i + 1][j - 1][to[k][1]][p | (to[k][1] == m)];
                        now = (now + dp[i][j][k][p]) % mod;
                    }
                }
            }
        }
    }
    int res = 0;
    for (int i = 0; i <= m; i++) res = (res + dp[2 * n][0][i][1]) % mod;
    printf("%d
", res);
    return 0;
}
原文地址:https://www.cnblogs.com/chd-acm/p/13618639.html