Educational Codeforces Round 81 (Rated for Div. 2) A-E

Educational Codeforces Round 81 (Rated for Div. 2)

A. Display The Number

\(n\)个区域,问最大能构成的数字是多少?

  • \(n\)为奇数:\(7111...\)
  • \(n\)为偶数:\(1111...\)
#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n, m, T;
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        if(n % 2 == 1 )
            printf("7");
        else
            printf("1");
        int len = n/2-1;
        for(int i = 1; i <= len; i++)
            printf("1");
        printf("\n");
    }
    return 0;
}

B. Infinite Prefixes

给一个长度为\(n\)的字符串\(s\),字符串\(t\)是字符串\(s\)的无限循环。\(cnt_0[q]\)表示在\(0-q\)之间有多少个\(0\)\(cnt_1[q]\)表示在\(0-q\)之间有多少个\(1\)。问\(cnt_0[q]-cnt_1[q] = x\)中,\(q\)的个数。

  • 首先循环一遍求出\(0-n\)位置上的值,并且\(a[n-1]\)为每次循环的变化。
  • \(a[i] + a[n-1] * temp =x(temp\ge 0)\)则当前位置\(i\)肯定能有一次变为\(x\)
  • 如果\(a[n-1]=0 \ \&\&\ a[i] = x\),那么则无限次。
#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n, m, T;
int a[maxn];
char ch[maxn];
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        scanf("%s", ch+1);
        int len = strlen(ch+1), cnt = 0;
        for(int i = 1; i <= len; i++){
            if(ch[i] == '0')
                a[i] = a[i-1]+1;
            else
                a[i] = a[i-1]-1;
        }
        int flag = 0;
        if(a[len] == 0){
            for(int i = 1; i <= len; i++){
                if(a[i] == m){
                    printf("-1\n");
                    flag = 1;
                    break;
                }
            }
            if(!flag)
                printf("0\n");
            continue;
        }
        int ans = 0;
        for(int i = 1; i <= len; i++){
            int x = (m-a[i])/a[len];
            if(x >= 0 && x *a[len] + a[i] == m)
                ans++;
        }
        if(m == 0)
            ans++;
        printf("%d\n", ans);
    }
    return 0;
}

C. Obtain The String

给你字符串\(s\)和字符串\(t\),每次可以从字符串\(s\)中取出一个子序列加到当前字符串\(z\)的末尾,字符串\(z\)初始为空。问最少取几次,字符串\(z\)可以变成字符串\(t\)
记录字符串\(s\)每个位置之后最近的每个字母的位置。暴力遍历即可。

#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n, m, T;
char s[maxn], t[maxn];
struct Node{
    int num[30];
}a[maxn];
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%s%s", s, t);
        int slen = strlen(s), tlen = strlen(t);
        mes(a[slen].num, -1);
        for(int i = slen-1; i >= 0; i--){
            int x = s[i] - 'a';
            a[i] = a[i+1];
            a[i].num[x] = i;
        }
        int ans = 0, cnt = 0;
        for(int i = 0; i < tlen; i++){
            int x = a[cnt].num[t[i]-'a'];
            if(x == -1){
                x = a[0].num[t[i]-'a'];
                if(x == -1){
                    ans = -1;
                    break;
                }
                else
                    ans++;
            }
            if(i == tlen-1)
                ans++;
            cnt = x+1;
        }
        printf("%d\n", ans);
    }
    return 0;
}

D. Same GCDs

问在\(n\le x\le n+m-1\)中存在几个\(x\)\(gcd(x, m) =gcd(n, m)\)

  • \(Gcd = gcd(n, m)\), 用唯一分解定理求出\(m/Gcd\)的质因子。
  • 用容斥定理算出\(n/Gcd\le x \le (n+m-1)/Gcd\)中为\(m/Gcd\)的质因子的倍数的个数\(tmp\)
  • 答案\(ans = (n+m-1)/Gcd -(n-1)/Gcd -tmp\)
    (感觉自己写的很麻烦不知道有没有简单一点的做法)
#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
ll n, m, ans, x;
int  T, cnt, num[maxn], p[maxn];
vector<ll> vec;
ll gcd(ll a, ll b){
    return b == 0 ? a: gcd(b, a%b);
}
void init(){
    mes(num , 0);
    cnt = 0;
    for(ll i = 2; i < maxn; i++){
        if(!num[i]){
            p[++cnt] = i;
            for(ll j = i*i; j < maxn; j+=i){
                num[j] = 1;
            }
        }
    }
}
void get(ll x){
    vec.clear();
    for(int i = 1; i <= cnt && p[i]*p[i] <= x; i++){
        if(x % p[i] == 0){
            vec.push_back(p[i]);
            while(x%p[i] == 0)
                x /= p[i];
        }
    }
    if(x > 1)
        vec.push_back(x);
}
ll dfs(int pre, int pos, ll num){
    ll tmp;
    for(int i = pre; i < vec.size(); i++){
        tmp = num * vec[i];
        if(pos & 1)
            ans += x/tmp;
        else
            ans -= x/tmp;
        dfs(i+1, pos+1, tmp);
    }
}
int main(){
    scanf("%d", &T);
    init();
    while(T--){
        scanf("%lld%lld", &n, &m);
        int len = sqrt(m);
        ll Gcd = gcd(n, m);
        get(m/Gcd);
        ans = 0;
        x = (n+m-1)/Gcd;
        dfs(0, 1, 1);
        ll tmp = x - ans;
        x = (n-1)/Gcd;
        ans = 0;
        dfs(0, 1, 1);
        tmp -= (x-ans);
        printf("%lld\n", tmp);
    }
    return 0;
}

E. Permutation Separation

把一个长度为\(n\)数组\(p\),分为两个非空集合\(p_1、p_2、p_3... p_k\)\(p_{k+1}、p_{k+2}...p_n\),可以随意移动第一个集合的元素到第二个集合,也可以移动第二个集合的元素到第二个集合。移动元素\(p_i\)所需要的花费为\(a_i\)。问让第一个集合的元素小于任意一个第二个集合的元素(其中一个集合为空集也可满足条件),最小的花费是多少?
首先能想到遍历所有的位置\(k\),从而找出最优解。那么要解决的问题是怎么在每次遍历的过程中快速的找到当前位置\(k\)的最优解。
如果当前位置中,要让第一个集合小于数字\(x\),第二个集合数字大于\(x\),则花费为第一个集合大于\(x\)的数的花费+第二个集合小于\(x\)的数的花费。因为中间数字为\(x\),那么\(x\)在哪个集合都是成立的,则当前不用管数字\(x\)的花费。
反过来说,如果数字\(x\)在第一个集合,而当前枚举的中间的数小于 \(x\)那么数字\(x\)肯定要移动到第二个集合。如果数字\(x\)在第二个集合,而当前枚举的中间的数大于 \(x\)那么数字\(x\)肯定要移动到第一个集合。
那么数字\(x\)在第一个集合的时候会对中间数为\([1,x-1]\)产生贡献。数字\(x\)在第二个集合的时候会对中间数为\([x+1,n]\)产生贡献。

  • 可以用线段树来维护枚举每个位置为中间数的最小值。
  • \(x\)在第一个集合,把\([1, x-1]\)的范围都加上\(a_i\)
  • \(x\)在第二个集合,把\([x+1, n]\)的范围都加上\(a_i\)
  • 遍历每个位置\(k\)找出最小值
#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 2e5+10;
const ll mod = 1e9+7;
int n;
ll lazy[maxn<<2];
ll node[maxn<<2], a[maxn];
int p[maxn];
void pushdown(int rt){
    if(lazy[rt]){
        node[rt<<1] += lazy[rt];
        node[rt<<1|1] += lazy[rt];
        lazy[rt<<1] += lazy[rt];
        lazy[rt<<1|1] += lazy[rt];
        lazy[rt] = 0;
    }
}
void update(int L, int R, ll c, int l, int r, int rt){
    if(R < L) return;
    if(L <= l && r <= R){
        node[rt] += c;
        lazy[rt] += c;
        return;
    }
    pushdown(rt);
    int mid = l+r>>1;
    if(L <= mid)
        update(L, R, c, l, mid, rt<<1);
    if(R > mid)
        update(L, R, c, mid+1, r, rt<<1|1);
    node[rt] = min(node[rt<<1], node[rt<<1|1]);
}
int main(){
    scanf("%d", &n);
    mes(node, 0);
    mes(lazy, 0);
    for(int i = 1; i <= n; i++)
        scanf("%d", &p[i]);
    for(int i = 1; i <= n; i++){
        scanf("%lld",&a[i]);
        update(p[i]+1, n, a[i], 1, n, 1);
    }
    ll ans = min(a[1], a[n]);
    for(int i = 1; i < n; i++){
        update(p[i]+1, n, -1*a[i], 1, n, 1);
        update(1, p[i]-1, a[i], 1, n, 1);
        ans = min(node[1], ans);
    }
    printf("%lld\n", ans);
    return 0;
}
posted @ 2020-01-30 13:41  竹攸  阅读(359)  评论(0编辑  收藏  举报