Educational Codeforces Round 48 (Rated for Div. 2)

又是回忆场。

A - Death Note

题意:有一本笔记,你在第i天连续写ai个字,每写满m个字强制翻页(不管还要不要写),求第i天翻了多少页。

题解:那么当前的页数就是下整+1(其实不加也可以,从0开始),直接模拟即可,我当时写的什么东西?

注意溢出。

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

void test_case() {
    int n, m;
    scanf("%d%d", &n, &m);
    ll cur = 0, px = 1;
    for(int i = 1; i <= n; ++i) {
        int ai;
        scanf("%d", &ai);
        cur += ai;
        ll cx = (cur / m) + 1;
        printf("%lld%c", cx - px, " \n"[i == n]);
        px = cx;
    }
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

B - Segment Occurrences

题意:给定两个字符串s,t,询问q次,每次询问s的子串[l,r]中出现了t多少次。

题解:很明显用KMP可以做,不能再鸽KMP了,弄个next数组,求出匹配的位置,然后差分打上标记,出现的次数就是(减去长度差异之后的)前缀和的差。

学了一个KMP,看起来还蛮对的样子,可以返回所有匹配的位置,特点是都是从0开始。

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

int pi[1005];
void GetPrefixFunction(char *s, int sl) {
    pi[0] = 0, pi[1] = 0;
    for(int i = 1, k = 0; i < sl; ++i) {
        while(k && s[i] != s[k])
            k = pi[k];
        pi[i + 1] = (s[i] == s[k]) ? ++k : 0;
    }
}

//返回t在s中所有occurrence的首地址,s和t都是从0开始的
int ans[1005], atop;
void KMP(char *s, int sl, char *t, int tl) {
    GetPrefixFunction(t, tl);
    atop = 0;
    for(int i = 0, k = 0; i < sl; ++i) {
        while(k && s[i] != t[k])
            k = pi[k];
        k += (s[i] == t[k]);
        if(k == tl)
            ans[++atop] = i - tl + 1;
    }
}

char s[1005], t[1005];
int prefix[1005];

void test_case() {
    int n, m, q;
    scanf("%d%d%d", &n, &m, &q);
    scanf("%s%s", s + 1, t + 1);
    KMP(s + 1, n, t + 1, m);
    for(int i = 1; i <= atop; ++i)
        ++prefix[ans[i] + 1];
    for(int i = 1; i <= n; ++i)
        prefix[i] += prefix[i - 1];
    while(q--) {
        int l, r;
        scanf("%d%d", &l, &r);
        if(r - l + 1 < m) {
            puts("0");
            continue;
        }
        printf("%d\n", prefix[r - m + 1] - prefix[l - 1]);
    }
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

C - Vasya And The Mushrooms

题意:有一个2*n的格子矩阵,从左上角出发,必须遍历每个格子恰好一次,求最大的分数。每个格子会长一种蘑菇,这种蘑菇有它的生长速度,每次移动没被遍历的格子的蘑菇就会生长这个积分。

题解:一旦在另一行的格子没有被遍历时,开始选择向右走,则会必须走到尽头然后折返。那么可以计算出这样做的总收益。否则要走向另一个格子,然后转移到下一列的决策。看起来就很dp!设dp[i][0/1]为从第i列的上/下开始走,遍历完右侧所有格子的最大收益。倒过来转移,每次转移分两种情况,(在上方时)一种是往下走然后加上右下角的dp值,还有因为这两步而增长的2倍后缀0的积分,另一种是直接往右走收掉后缀1的积分,然后到尽头转头收掉后缀2的积分,同时加上走后缀1时后缀2增长的积分。看起来很像我第一次学线段树的那道题。

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

ll a[300005][2];
ll suffix0[300005][2];
ll suffix1[300005][2];
ll suffix2[300005][2];
ll dp[300005][2];

void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%lld", &a[i][0]);
    for(int i = 1; i <= n; ++i)
        scanf("%lld", &a[i][1]);
    for(int i = n; i >= 1; --i) {
        suffix0[i][0] = suffix0[i + 1][0] + a[i][0];
        suffix0[i][1] = suffix0[i + 1][1] + a[i][1];
        suffix1[i][0] = suffix1[i + 1][0] + suffix0[i + 1][0] + a[i][0];
        suffix1[i][1] = suffix1[i + 1][1] + suffix0[i + 1][1] + a[i][1];
        suffix2[i][0] = suffix2[i + 1][0] + 1ll * (n - i + 1) * a[i][0];
        suffix2[i][1] = suffix2[i + 1][1] + 1ll * (n - i + 1) * a[i][1];
    }

    for(int i = n; i >= 1; --i) {
        dp[i][0] = max(dp[i + 1][1] + 2ll * suffix0[i + 1][0] + 2ll * suffix0[i + 1][1] + a[i][1], suffix1[i + 1][0] + suffix2[i][1] + 1ll * (n - i) * suffix0[i][1]);
        dp[i][1] = max(dp[i + 1][0] + 2ll * suffix0[i + 1][1] + 2ll * suffix0[i + 1][0] + a[i][0], suffix1[i + 1][1] + suffix2[i][0] + 1ll * (n - i) * suffix0[i][0]);
    }

    printf("%lld\n", dp[1][0]);
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

D - Vasya And The Matrix

题意:给n行的异或和,m列的异或和,把这个矩阵复原出来,或者告知无解。

题解:这种题貌似都可以留给最后一行去调整的?首先每个位之间是不影响的,全部分开。设想下面的一种构造。

0  0  0  0  0  a1
0  0  0  0  0  a2
0  0  0  0  0  a3
b1 b2 b3 b4 b5 ?

最后的问号处要求a4==b6则有解,否则我们翻转任意一位都会使得那一行和那一列的异或和同时改变,最后右下角这个还是没变。

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

int a[105], b[105];

void test_case() {
    int n, m;
    scanf("%d%d", &n, &m);
    int sumr = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        sumr ^= a[i];
    }
    int sumc = 0;
    for(int i = 1; i <= m; ++i) {
        scanf("%d", &b[i]);
        sumc ^= b[i];
    }
    if(sumr == sumc) {
        puts("YES");
        for(int i = 1; i <= n - 1; ++i) {
            for(int j = 1; j <= m - 1; ++j) {
                printf("0 ");
            }
            printf("%d\n", a[i]);
        }
        for(int j = 1; j <= m - 1; ++j)
            printf("%d ", b[j]);
        printf("%d\n", sumr ^ b[m]^a[n]);
    } else
        puts("NO");
}

int main() {
#ifdef KisekiPurin
    freopen("KisekiPurin.in", "r", stdin);
#endif // KisekiPurin
    int t = 1;
    for(int ti = 1; ti <= t; ++ti) {
        //printf("Case #%d: ", ti);
        test_case();
    }
}

E - Rest In The Shades

CF居然也有几何题,得做了。

题意:x轴下方有一个移动光源平行x轴移动,x轴有一些线段。x轴上方有一些点,求每个点在阴影中的时间。

题解:点和线段的数量太多了,暴力搞不得。但是把点和光源的起点终点连线,然后中间完整的线段部分可以直接等比例计算(中间完整的阴影的长度/交点x轴长度),然后加上两边的(假如有交)就可以了。线段已经排序且不会相交,则每个询问直接二分就可以了。

posted @ 2019-11-21 11:33  KisekiPurin2019  阅读(144)  评论(0编辑  收藏  举报