Educational Codeforces Round 81 (Rated for Div. 2) Jan/29/2020 22:35UTC+8

比赛链接 https://codeforces.com/contest/1295
比赛记录 https://blog.csdn.net/cheng__yu_/article/details/105395197

B. Infinite Prefixes(前缀和)

在这里插入图片描述
链接:https://codeforces.com/contest/1295/problem/B

题意:给定一个长度为 n 的 01 串 s ,可以无限叠加。给定一个 x ,问 c n t 0 , i − c n t 1 , i = x cnt_{0,i}-cnt_{1,i}=x cnt0,icnt1,i=x 的位置个数。 c n t 0 , i cnt_{0,i} cnt0,i表示 [ 1 , i ] [1,i] [1,i] 中 0 的个数。 c n t 1 , i cnt_{1,i} cnt1,i表示 [ 1 , i ] [1,i] [1,i] 中 1 的个数。如果有无限个位置相等,输出 -1 ,否则输出个数。(注意空串也算一个)

思路

  • 首先想到一个串的累加量,也就是 pref[n] 。
  • 如果累加量为 0,那么如果出现和 x 相等的 pref[i] ,那么就会出现无限个相等的位置。
  • 如果累加量不为 0 ,那么 x x x 和 位置 i i i 的值 p r e f [ i ] pref[i] pref[i] 必须相隔了 正整数个 p r e f [ n ] pref[n] pref[n],才能不断累加从 p r e f [ i ] pref[i] pref[i] 变成 x x x
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int t,n,x;
int a[maxn],pref[maxn];
string s;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>n>>x>>s;
        s="0"+s;
        for(int i=1; i<=n; ++i)
            pref[i]=pref[i-1]+(s[i]=='1'?-1:1);
        if(pref[n]==0)
        {
            bool ok=1;
            for(int i=1; i<=n; ++i)
                if(pref[i]==x) ok=0;
            puts(ok?"0":"-1");
        }
        else
        {
            int cnt=0;
            if(x==0) cnt++;
            for(int i=1; i<=n; ++i)
                if((x-pref[i])%pref[n]==0&&(x-pref[i])/pref[n]>=0) cnt++;
            printf("%d\n",cnt);
        }
    }
    return 0;
}

C. Obtain The String(预处理)

在这里插入图片描述
题意:给定字符串 s 和 t 。问从 s 中选取子序列构造成 t 需要几次

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5,maxm=1e5+5;
const int mod=998244353,inf=0x7f7f7f7f;

int tt;
string s,t;
int nxt[maxn][30];

int main()
{
    cin>>tt;
    while(tt--)
    {
        cin>>s>>t;
        int n=s.size(),m=t.size();
        s="0"+s,t="0"+t;
        for(int j=1;j<=26;++j)
            nxt[n][j]=-1;
        for(int i=n-1;i>=0;--i)
        {
            for(int j=1;j<=26;++j)
                nxt[i][j]=nxt[i+1][j];
            int x=s[i+1]-'a'+1;
            nxt[i][x]=i+1;
        }

        int ans=1,pos=0;
        bool ok=true;
        for(int i=0;i<m;++i)
        {
            int x=t[i+1]-'a'+1;
            if(nxt[pos][x]!=-1)
                pos=nxt[pos][x];
            else
            {
                ans++;
                pos=0;
                if(nxt[pos][x]==-1)
                {
                    ok=false;
                    break;
                }
                pos=nxt[pos][x];
            }
        }
        if(ok)
            printf("%d\n",ans);
        else
            puts("-1");
    }
    return 0;
}

D. Same GCDs

在这里插入图片描述

链接:https://codeforces.com/contest/1295/problem/D

题意:给定 a、m ,计算 ∑ x = 1 m − 1 [ g c d ( a , m ) = g c d ( a + x , m ) ] \sum_{x=1}^{m-1} [gcd(a,m)=gcd(a+x,m)] x=1m1[gcd(a,m)=gcd(a+x,m)] ( 1 ≤ a < ≤ m ≤ 1 0 10 ) (1\le a < \le m \le 10^{10}) (1a<m1010)

思路

∑ x = 0 m − 1 [ g c d ( a , m ) = g c d ( a + x , m ) ] \sum_{x=0}^{m-1} [gcd(a,m)=gcd(a+x,m)] x=0m1[gcd(a,m)=gcd(a+x,m)]

根据更相减损法有
∑ x = 0 m − 1 [ g c d ( a , m ) = g c d ( ( a + x ) m o d   m , m ) ] \sum_{x=0}^{m-1} [gcd(a,m)=gcd((a+x) mod~ m,m)] x=0m1[gcd(a,m)=gcd((a+x)mod m,m)]

这里相当于平移了一下

∑ i = 0 m − 1 [ g c d ( a , m ) = g c d ( i , m ) ] \sum_{i=0}^{m-1} [gcd(a,m)=gcd(i,m)] i=0m1[gcd(a,m)=gcd(i,m)]

d = g c d ( a , m ) d=gcd(a,m) d=gcd(a,m)

∑ i = 0 m − 1 [ g c d ( i , m ) = d ] \sum_{i=0}^{m-1} [gcd(i,m)=d] i=0m1[gcd(i,m)=d]

这里发现 i 、m 是在枚举 d 的倍数,因此化简为:

∑ i = 1 ⌊ m − 1 d ⌋ [ g c d ( i , m d ) = 1 ] \sum_{i=1}^{ \lfloor \frac {m-1}d \rfloor} [gcd(i,\frac md)=1] i=1dm1[gcd(i,dm)=1]

因此这个式子就相当于 φ ( m d ) \varphi (\frac md) φ(dm)

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+5;
int t;
ll a,m;
int main()
{
    cin>>t;
    while(t--)
    {
        cin>>a>>m;
        ll d=__gcd(a,m);
        ll n=m/d;
        ll ans=n;
        for(ll i=2; i*i<=n; ++i)
        {
            if(n%i==0)
            {
                ans=ans/i*(i-1);
                while(n%i==0) n/=i;
            }
        }
        if(n>1) ans=ans/n*(n-1);
        cout<<ans<<"\n";
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值