Educational Codeforces Round 81 (Rated for Div. 2)(A~D)

A. Display The Number(水题)

分析

分类讨论下一下,

代码

#include <iostream>
#include <algorithm>
#include <map>
#include <set>
#include <list>
#include <queue>
#include <deque>
#include <cmath>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
#define ios ios::sync_with_stdio(false) 
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353

const int mxn = 2e5 + 10;
int ar[mxn];


int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int n;
        scanf("%d", &n);
        if(n % 2)
        {
            for(int i = 1; i <= n/2; i ++)
            {
                if(i == 1)
                    printf("7");
                else
                    printf("1");

            }
            printf("\n");
        }
        else
        {
            for(int i = 1; i <= n/2; i ++)
            {
                printf("1");

            }
            printf("\n");

        }
    }

    return 0;
}


B. Infinite Prefixes(分类讨论?模拟?周期?)

分析

  • 题意
  1. 给我们一个长度为n的仅由0,1字符组成的字符串 a a a,将这个字符串无限拼接到一个空串尾部之后形成一个无限序列 s s s
  2. 定义:关于序列s的中某个位置的前缀差值 p r e [ i ] pre[i] pre[i]为1~i之间0字符的数量 − - 1字符的数量…
  3. 现在给们一个前缀差值x,问满足 p r e [ i ] = = x pre[i]==x pre[i]==x的i位置有多少个?
  4. 如果有无限个满足题意的位置,输出-1,否则输出所有满足题意的位置
  • 分析
  1. 这一题在脑子中想,就被可能的复杂情况给弄晕了,所以我们要选择分类讨论,把复杂的问题化的简单一点,但是也不能分的太细,否则情况多的自己又迷了,,,要分的有条理,,
  2. 而且s是一个周期循环序列,我们应该尽量 利用它的周期性质,最后就是利用 前缀和 维护数据,
  3. 最后 过程直接看代码 吧

代码

#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353

const int mxn = 2e5 + 10;
char ar[mxn];
int pre[mxn];

int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        int n, x;
        scanf("%d %d", &n, &x);
        scanf("%s", ar + 1);
        for(int i = 1; i <= n; i ++)
        {
            pre[i] = pre[i - 1];
            if(ar[i] == '0') pre[i] ++; 
            else             pre[i] --;
        }

        int all = pre[n];               //周期增量
        int ans = 0;
        if(x == 0) ans ++;              //空串肯定满足答案
        if(all == 0)
        {
            if(x == 0)                  //一个周期至少一个,有无穷个周期,所以无穷个
            {
                printf("-1\n");
                continue;
            }

            for(int i = 1; i <= n; i ++)
            {
                if(pre[i] == x)
                {
                    ans ++;
                    break;
                }
            }
            if(ans)                     //如果ans>0表示每个周期至少有1个答案,无数个周期无数个答案
                printf("-1\n");
            else
                printf("0\n");
        }
        else			//如果周期增量all != 0
        {
            for(int i = 1; i <= n; i ++)
            {
                if((x - pre[i]) % all == 0 && (x - pre[i]) / all >= 0)
                    ans ++;
            }
            printf("%d\n", ans);
        }
    }



    return 0;
}

C. Obtain The String(暴力模拟+贪心)

分析

  • 题意
  1. 给我们两个字符串s、t,对于每次操作我们都可以从s中选择一个子序列把它拼接到一个空串到尾部,问经过一些合理次数的这样操作,会产生一个新的子串a,能否通过合理的操作之后使 a与t相同,如果可以的话,最少需要多少次这样的操作?,如果不能输出-1
  • 分析
  1. 首先判断 t中的字符是否都在s中出现了,之后都出现了才能通过一定次数的操作拼接出t
  2. 我们思路是,模拟 每次操作从s中合理选择子序列的过程,在模拟的过程中统计一下操作次数,而且在 某次操作过程中我们应该选择尽可能多的字符从s中(这样好耗费的次数才可能更少)
  3. 对于这个模拟的过程我们要注意,假如我们在s中某个位置(设置个位置为i)选择字符之后,下次我们再次进行选择字符的时候,只能在i位置之后的位置字符中挑选合适的字符,而这个找 i位置之后的位置 的这个过程 则是通过 二分 来快速确定下一个位置的,,,
  4. 主要思路就是这,剩下的看代码

代码

#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353

const int mxn = 2e5 + 10;
vector<int> v[26];
int p[26];

void init()
{
    for(int i = 0; i < 26; i ++)
        v[i].clear();
    fill(p, p + 26, 0);
}

int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        init();
        string  s, t;
        cin >> s >> t;
        for(int i = 0; i < s.size(); i ++)
            v[s[i] - 'a'].pb(i) ;
        int fg = 1;
        for(auto x : t)
        {
            if(v[x - 'a'].size() == 0)
            {
                fg = 0; break;
            }
        }

        if(! fg)
            printf("-1\n");
        else
        {
            int ct = 0;
            for(int i = 0; i < t.size(); )
            {
                int last = -1;

                int pos = upper_bound(v[t[i] - 'a'].begin(), v[t[i] - 'a'].end(), last) - v[t[i] - 'a'].begin();
                while(pos != v[t[i] - 'a'].size() && i < t.size())
                {
                    last = v[t[i] - 'a'][pos];
                    p[t[i] - 'a'] = pos + 1;
                    i ++;
                    if(i >= t.size()) break;
                    pos = upper_bound(v[t[i] - 'a'].begin() + p[t[i] - 'a'], v[t[i] - 'a'].end(), last) - v[t[i] - 'a'].begin();
                }
                ct ++;
                fill(p, p + 26, 0);
            }
            printf("%d\n", ct);
        }
    }

    return 0;
}

D. Same GCDs(欧拉函数+gcd性质应用)

分析

  • 题意
  1. 给我们a、m、x三个变量( 1 < = a < = m < = 1 e 10 1<=a<=m<=1e10 1<=a<=m<=1e10、0<=x < m请注意x的取值范围标红的的部分,这个不是偶然,是有作用的,后面会分析到),
  2. 又给我们一个等式 g c d ( a , m ) = = g c d ( a + x , m ) gcd(a,m)==gcd(a+x,m) gcd(a,m)==gcd(a+x,m),问x的取值有多少个使该等式成立?,,输出符合题意的x的数量
  • 分析
  1. 首先我们看到数据的取值范围很大,只要要用 O ( n 1 / 2 ) O(n^{1/2}) O(n1/2)或者 O ( l g ( n ) ) O(lg(n)) O(lg(n))的算法来解决这个问题,
  2. 接下来是我们对所给的等式的变形:
    1. 根据最大公约数的性质,如果a >=b,那么 g c d ( a , b ) = = g c d ( a − b , b ) gcd(a,b)==gcd(a-b,b) gcd(a,b)==gcd(ab,b),换句话说可以写成: g c d ( a , b ) = g c d ( a % b , b ) gcd(a,b)=gcd(a\%b,b) gcd(a,b)=gcd(a%b,b)
    2. 另一个性质:如果 g c d ( a , b ) = d gcd(a,b)=d gcd(a,b)=d,则这个等式可以变化为: g c d ( a / d , b / d ) = 1 gcd(a/d,b/d)=1 gcd(a/d,b/d)=1
    3. g c d ( a , m ) = d gcd(a,m)=d gcd(a,m)=d,那么 g c d ( a + x , m ) = g c d ( ( a + x ) % m , m ) = d gcd(a+x,m)=gcd((a+x)\%m,m)=d gcd(a+x,m)=gcd((a+x)%m,m)=d根据性质1),我们接下来根据性质2把等式 g c d ( ( a + x ) % m , m ) = d   gcd((a+x)\%m,m)=d~ gcd((a+x)%m,m)=d ,转化为下面等式: g c d [ ( a + x ) % m / d ,    m / d ] = 1 gcd[(a+x)\%m/d,~~m/d]=1 gcd[(a+x)%m/d,  m/d]=1
    4. 根据这个等式我们可以确定 ( a + x ) % m / d (a+x)\%m/d (a+x)%m/d    m / d ~~m/d   m/d互为质数,接下来我们就可以应用 欧拉函数的性质来解题,
    5. 欧拉发现一个快速求 n n n与位于 [ 0 , n ) [0,n) [0,n)之间的数字互为质数的个数,从欧拉函数这个性质的描述中我们可以发现,这个性质求解的互为质数数量,是有限制区间的就是 之前蓝色字体部分
    6. 那么要么要想应用 “欧拉函数的性质” 就要满足人家的限制条件,还记得在 题意叙述 中特别强调的 x的取值范围 0 < = x < m 0<=x<m 0<=x<m吗?
      1. 我们考虑 g c d ( a + x , m ) gcd(a+x,m) gcd(a+x,m)中,如果 a < = a + x < m a<=a+x<m a<=a+x<m的时候,这个时候 ( a + x ) % m (a+x)\%m (a+x)%m的取值区间是不变的所以,这个时候 ( a + x ) % m (a+x)\%m (a+x)%m的取值范围为 [ a , m ) [a,m) [a,m);
      2. 如果 m < = a + x < a + m m<=a+x< a+m m<=a+x<a+m 的时候,但是在根据性质1做变换的之后, ( a + x ) (a+x)%m (a+x)的取值区间为[0,a-1].
      3. 好了我们将1,2步骤中的两个绿色区间拼凑起来为[0,a-1]+[a,m)=[0,m),这个最终拼凑出来的区间就正好满足欧拉函数的性质,,,,同理我们可以推出 ( a + x ) % m / d (a+x)\%m/d (a+x)%m/d的取值区间为: [ 0 , m / d ) [0,m/d) [0,m/d),那么下面之前我们变换出的等式,就可使用“欧拉函数的性质”了 g c d [ ( a + x ) % m / d ,    m / d ] = 1 gcd[(a+x)\%m/d,~~m/d]=1 gcd[(a+x)%m/d,  m/d]=1
  3. 至于怎么用“欧拉函数的性质”就靠自己学习一下的了,,终于说完了

代码

#include <bits/stdc++.h>
using namespace std;
void fre() { freopen("A.txt", "r", stdin); freopen("Ans.txt","w",stdout); }
void Fre() { freopen("A.txt", "r", stdin);}
#define ios ios::sync_with_stdio(false)
#define Pi acos(-1)
#define pb push_back
#define fi first
#define se second
#define ll long long
#define db double
#define INF 0x3f3f3f3f
#define mod 998244353

const int mxn = 2e5 + 10;
char ar[mxn];
int pre[mxn];


ll euler(ll n)
{
    ll res = n;
    for(ll i = 2; i * i <= n; i ++)
    {
        if(n % i == 0)
        {
            res = res / i * (i - 1);
            while(n % i == 0)   
                n /= i;
        }
    }
    if(n != 1)
        res = res / n * (n - 1);
    return res;
}

ll gcd(ll a, ll b) { return b == 0 ? a : gcd(b, a % b); }



int main()
{
    /* fre(); */
    int T;
    scanf("%d", &T);
    while(T --)
    {
        ll a, m;
        scanf("%lld %lld", &a, &m);
        ll d = gcd(a, m);
        ll ans = euler(m / d);
        printf("%lld\n", ans);
    }

    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值