Educational Codeforces Round 81 (Rated for Div. 2)

QAQ

A. Display The Number

思路:

题目说了液晶屏最多有998244353位数,观察所有数字的液晶显示,只有1所用的线段最少,即使我们每一位都放1,最多也只有1e5/2位数,那么题目就变得简单了,我们优先凑一来增加所得数的位数,假如最后还剩下一根线段,我们可以把最高位变成7。

代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        scanf("%d",&n);
        int d = n/2;
        if(n%2==1)
            printf("7");
        else
            printf("1");
        for(int i = 1;i<d;++i)
            printf("1");
        printf("
");
    }
    return 0;
}

B. Infinite Prefixes

思路:

假设一个前缀ibalance值为pre[i],它的值可以表示为k*c+pre[i%n](其中c为一个串总的balance),对结果进行分类讨论。

如果c0的话

假如在一个s串中有值能达到x,则结果显然是无限的,否则,答案就是0

如果c不是零的话,则可以枚举一个s的每个位置i,计算一下x = k*c+pre[i]是否存在整数k使方程成立,如果存在,则ans++

代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long li;

int n, x;
string s;

inline bool read() {
    if(!(cin >> n >> x >> s))
        return false;
    return true;
}

inline void solve() {
    int ans = 0;
    bool infAns = false;
    
    int cntZeros = (int)count(s.begin(), s.end(), '0');
    int total = cntZeros - (n - cntZeros);
    
    int bal = 0;
    for(int i = 0; i < n; i++) {
        if(total == 0) {
            if(bal == x)
                infAns = true;
        }
        else if(abs(x - bal) % abs(total) == 0) {
            if((x - bal) / total >= 0)
                ans++;
        }
        
        if(s[i] == '0')
            bal++;
        else
            bal--;
    }
    
    if(infAns) ans = -1;
    cout << ans << endl;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cout << fixed << setprecision(15);
    int tc; cin >> tc;
    while(tc--) {
        read();
        solve();
    }
    
    return 0;
}

C. Obtain The String

思路:

用一个set数组去存储s串每个字母出现的位置,从头遍历t串,另外用一个数记录下当前选用来构成z串所用字母的位置,假如在当前这次已经没有办法去找到符合条件的字母,那就代表需要在找一个s串来继续z串的填充,ans++,最后输出答案即可。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
set<int> st[30];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        for(int i =0; i<26; i++)
            st[i].clear();
        //memset(tmp,0,sizeof(tmp));
        string s,t;
        cin >> s >> t;
        int len = s.size();
        for(int i = 0; i<len; ++i)
        {
            st[s[i]-'a'].insert(i);
        }
        len  =t.size();
        bool flag = false;
        LL ans =1;
        int tmp = 0;
        for(int i = 0; i<len; ++i)
        {
            int d = t[i]-'a';
            if(st[d].size()==0)
            {
                flag = true;
                break;
            }
            else
            {
                set<int>::iterator it = st[d].lower_bound(tmp);
                //cout << d << endl;
                //cout << *it <<" " << tmp << endl;
                if(it!=st[d].end())
                {
                    tmp = (*it)+1;
                }
                else
                {
                    ++ans;
                    tmp = 0;
                    --i;
                }
                //cout << "**" << ans << endl;
            }
        }
        if(flag)
        {
            printf("-1
");
        }
        else
            cout << ans << "
";
    }
    return 0;
}

D. Same GCDs

思路:

g = gcd(a,m)

D题的答案就是Eular(m/g);证明如下,

 

 

同时,因为

所以变换之后a+x的取值区间就变成了[0,m)

那么(a+x)/g的区间就是[0,m/g)

整个式子就是求[0,m/g)中与m/g互素的数的个数,就是Eular(m/g)的值。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL Eular(LL n)
{
    LL ans = n;
    for(LL i = 2;i*i<=n;++i)
    {
        if(n%i==0)
        {
            ans-=(ans/i);
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1) ans-=ans/n;
    return ans;
}
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        LL a,b;
        cin >>a >> b;
        LL g = __gcd(a,b);
        cout << Eular(b/g) << endl;
    }
    return 0;
}

E. Permutation Separation

思路:

首先我们先假设分割的位置为i,长度为x,那么最终答案是多少?我们需要把1-x放进左边的集合,x+1-n放到右面的集合,所以总花费可以这样计算,令pos = x在原排列中的位置,对于i<=pos的部分来说,只要它的值比x大,它就需要移到右边,花费就需要加上这个数的权值。同样,对于i>pos的数来说,只要它的值比x小,他就需要移到左边,花费就需要加上它的权值。

接下来我们考虑优化一下当前的算法。我们先求出前缀和pre,从x = 1.i = 1开始枚举,外层循环枚举x,内层循环枚举i,我们发现对于每个i<pos(x),答案都是pre[i]+a[pos(x)],而对于i>=pos(x),答案就变成了pre[i]-a[pos(x)],在x1的情况下就是上面两个式子所有取值的最小值。接下来x = 2,也是同样的道理,可以发现,事实上假如我们把前缀和作为节点的值建一棵线段树,上面的内层循环就变成的区间加上一个数以及求最小值。这样复杂度就由O(n^2)到了O(nlogn)。题目可解。

代码:

#include <bits/stdc++.h>
using namespace std;
#define MAXN 200000+10
#define LL long long
int n;
int p[MAXN];
int rp[MAXN];
int a[MAXN];
LL b[MAXN];
long long t[4*MAXN];
LL add[4*MAXN];
void build(int v,int l,int r)
{
    if(r-l==1)
    {
        t[v] = b[l];
        return ;
    }
    int mid = (l+r)/2;
    build(v*2+1,l,mid);
    build(v*2+2,mid,r);
    t[v] = min(t[v*2+1],t[v*2+2]);
    return ;
}
void push(int v,int l,int r)
{
    if(add[v]!=0)
    {
        if(r-l>1)
        {
            for(int i = 2*v+1;i<2*v+3;++i)
            {
                add[i]+=add[v];
                t[i]+=add[v];
            }
        }
        add[v] = 0;
    }
}
void upd(int v,int l,int r,int L,int R,int x)
{
    if(L>=R)
        return ;
    if(l==L&&r==R)
    {
        add[v]+=x;
        t[v]+=x;
        push(v,l,r);
        return ;
    }
    push(v,l,r);
    int mid = (l+r)/2;
    upd(v*2+1,l,mid,L,min(mid,R),x);
    upd(v*2+2,mid,r,max(L,mid),R,x);
    t[v] = min(t[v*2+1],t[v*2+2]);
}
void upd(int l,int r,int x)
{
    upd(0,0,n,l,r,x);
}
LL get(int v,int l,int r,int L,int R)
{
    if(L>=R)
        return 1e18;
        push(v,l,r);
    if(l==L&&r==R)
        return t[v];
    int mid = (l+r)/2;
    return min(get(v * 2 + 1, l, mid, L, min(R, mid)),
           get(v * 2 + 2, mid, r, max(L, mid), R));
}
LL get(int l,int r)
{
    return get(0,0,n,l,r);
}
int main()
{
    scanf("%d",&n);
    for(int i = 0;i<n;++i)
    {
        scanf("%d",p+i);
        --p[i];
        rp[p[i]] = i;
    }
    for(int i = 0;i<n;++i)
        scanf("%d",a+i);
    b[0] = a[0];
    for(int i = 1;i<n;++i)
        b[i] = a[i]+b[i-1];
    build(0,0,n);
    LL res = get(0,n-1);
    for(int i = 0;i<n;++i)
    {
        int pos = rp[i];
        upd(pos,n,-a[pos]);
        upd(0,pos,a[pos]);
        res = min(res,get(0,n-1));
    }
    cout <<res <<endl;

    return 0;
}
原文地址:https://www.cnblogs.com/baihualiaoluan/p/12291412.html