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("
");
    }
    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(tempge 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
");
                    flag = 1;
                    break;
                }
            }
            if(!flag)
                printf("0
");
            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
", 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
", ans);
    }
    return 0;
}

D. Same GCDs

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

  • (Gcd = gcd(n, m)), 用唯一分解定理求出(m/Gcd)的质因子。
  • 用容斥定理算出(n/Gcdle 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
", 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
", ans);
    return 0;
}
原文地址:https://www.cnblogs.com/zhuyou/p/12242631.html