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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章