码迷,mamicode.com
首页 > 其他好文 > 详细

Codeforces Round #506 (Div. 3)

时间:2020-01-31 10:23:50      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:href   line   左移   扫描线   inline   second   code   多少   有序   

题目链接:https://codeforces.com/contest/1029

A - Many Equal Substrings

题意:给一个长度为 \(n(1\leq n\leq50)\) 的字符串 \(t\) ,和一个正整数 \(k(1\leq k\leq50)\) ,要求构造一个最短的字符串 \(s\) ,使得 \(t\)\(s\) 中恰好出现 \(k\) 次。

题解:看起来就像KMP算法,来个 \(O(n^3)\) 的做法就可以了,枚举重叠部分的两个开始位置,然后判断是否重叠,用此找出最长的重叠位置。但是直接复制一个前缀函数来用最简单。

在模板中提供的前缀函数是要求从0开始的,而 \(\pi(n)\) 即为整个字符串除去字符串本身的首尾重叠的最长长度。

注意留够空间。

int pi[3005];
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;
    }
}

char s[3005];

void test_case() {
    int n, k;
    scanf("%d%d%s", &n, &k, s);
    GetPrefixFunction(s, n);
    int p = pi[n];
    int top = n;
    --k;
    while(k--) {
        for(int i = 0; i < n - p; ++i) {
            s[top] = s[top - (n - p)];
            ++top;
        }
    }
    s[top] = '\0';
    puts(s);
}

B - Creating the Contest

题意:给一个长度为 \(n(1\leq n \leq 2\cdot 10^5)\) 的严格单调递增的数列,要求从中选出若干个数,使得除了最大的那个数外,每个数都存在一个比它大且不超过它的两倍的数。

题解:很显然最容易满足“比某个数大且不超过它的两倍”的是它的下一个数,意思是一旦断掉就要重新开始咯。

int a[200005];

void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    int ans = 1, cur = 1;
    for(int i = 2; i <= n; ++i) {
        if(a[i] > a[i - 1] * 2)
            cur = 1;
        else {
            ++cur;
            ans = max(ans, cur);
        }
    }
    printf("%d\n", ans);
}

C - Maximal Intersection

题意:给 \(n(2\leq n \leq 3\cdot 10^5)\) 条线段,移除恰好一条线段使得剩下的 \(n-1\) 条线段的交尽可能长。

题解:上次不是做过一个矩形版的吗?我还是用扫描线做的。这个直接维护线段的前缀交和后缀交就可以了。

int l[300005], r[300005];
int pl[300005], pr[300005];
int sl[300005], sr[300005];

void test_case() {
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d%d", &l[i], &r[i]);
    pl[0] = -INF, pr[0] = INF;
    for(int i = 1; i <= n; ++i) {
        pl[i] = max(pl[i - 1], l[i]);
        pr[i] = min(pr[i - 1], r[i]);
    }
    sl[n + 1] = -INF, sr[n + 1] = INF;
    for(int i = n; i >= 1; --i) {
        sl[i] = max(sl[i + 1], l[i]);
        sr[i] = min(sr[i + 1], r[i]);
    }
    int ans = 0;
    for(int i = 1; i <= n; ++i) {
        int L = max(pl[i - 1], sl[i + 1]);
        int R = min(pr[i - 1], sr[i + 1]);
        //printf("L=%d R=%d\n", L, R);
        ans = max(ans, R - L);
    }
    printf("%d\n", ans);
}

D - Concatenated Multiples

题意:给 \(n(2\leq n \leq 2\cdot 10^5)\) 个正整数和一个正整数 \(k\) ,求有多少个有序数对 \((i,j) (i\neq j)\) 使得第 \(i\) 个数后面接上第 \(j\) 个数之后被 \(k\) 整除。

题解:处理出每个数模 \(k\) 的余数,分长度存储在map里面。然后枚举第一个数和第二个数的长度(第一个数左移的量),就可以在对应的map里面查到有多少个数满足题意了。最后把 \((i,i)\) 的情况去掉。

注意:1e9接在1e9后面是大概1e19,会溢出。要考虑模运算对四则运算的等价变换。

int a[200005];
map<int, int> m[11];

int GetLen(int x) {
    int len = 1;
    while(x >= 10) {
        x /= 10;
        ++len;
    }
    return len;
}

ll GetXX(int x, int len, int k) {
    ll cur = 1;
    while(len--)
        cur *= 10;
    cur += 1;
    cur %= k;
    cur *= x;
    cur %= k;
    return cur;
}

void test_case() {
    int n, k;
    scanf("%d%d", &n, &k);
    ll ans = 0;
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        int len = GetLen(a[i]);
        m[len][a[i] % k]++;
        if(GetXX(a[i], len, k) == 0)
            --ans;
    }
    for(int i = 1; i <= n; ++i) {
        ll cur = a[i];
        for(int j = 1; j <= 10; ++j) {
            cur *= 10;
            cur %= k;
            int t = (k - cur) % k;
            auto it = m[j].find(t);
            if(it != m[j].end())
                ans += (*it).second;
        }
    }
    printf("%lld\n", ans);
}

Codeforces Round #506 (Div. 3)

标签:href   line   左移   扫描线   inline   second   code   多少   有序   

原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12244534.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!