Educational Codeforces Round 81 (Rated for Div. 2)

青春壹個敷衍的年華 提交于 2020-01-30 15:52:51

比赛传送门

A. Display The Number

题意:

在这里插入图片描述
给你n(2n105)n(2\leqslant n\leqslant 10^{5})根火柴,问最大能拼成的数字。
 

思路:

由于其他数字的火柴都要44个以上,除了11需要22根,77需要33根,所以拼其他数字的话不如拼两个11,而33根火柴的时候拼11不如拼77
所以当nn为偶数时,全部拼11
nn为奇数时,多出来33根拼77,其他全部拼11
 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
 
int main()
{
    ll T;
    cin>>T;
    while(T--)
    {
        ll n;
        cin>>n;
        if(n%2==0)
        {
            for(int i=1;i<=n/2;i++)putchar('1');
            putchar('\n');
        }
        else
        {
            putchar('7');
            for(int i=1;i<=n/2-1;i++)putchar('1');
            putchar('\n');
        }
    }
    return 0;
}

 
 

B. Infinite Prefixes

竟然被B题卡了,有点无语,后来理思路过了。

题意:

给你一个长度为n(1n105)n(1\leqslant n\leqslant 10^{5})的串ss,定义串ttss的无限循环。
定义一个前缀的平衡值为前缀中00的个数减11的个数,空串算一个前缀。
给定一个x(109x109)x(-10^{9}\leqslant x \leqslant 10^{9})
问有多少个前缀的平衡值等于xx
 

思路:

我们先处理出整个串ss的平衡值pp
定义ss串中长度为ii的前缀的平衡值为wiw_{i}
于是我们要求的就是方程kp+wi=xkp+w_{i}=x中,kk有多少个自然数解。
先把p=0p=0的情况判掉,遍历一遍如果存在wi=xw_{i}=x则有无穷个,否则有00个。
p0p\neq 0时,就得到了k=xwipk=\frac{x-w_{i}}{p}
然后遍历ii,对于每一个wiw_{i},若xwix-w_{i}能被pp整除,且计算出来的kk为自然数,则答案加11
最后x=0x=0时,因为空串要考虑,所以答案再加11
 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
 
 
string s;
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        ll n,x;
        cin>>n>>x>>s;
        ll p=0;
        for(ll i=0;i<s.size();i++)
        {
            if(s[i]=='0')p++;
            else p--;
        }
        if(p==0)
        {
            ll ans=0,w=0;
            for(ll i=0;i<s.size();i++)
            {
                if(s[i]=='0')w++;
                else w--;
                if(w==x)ans++;
            }
            if(ans)cout<<-1<<endl;
            else cout<<0<<endl;
            continue;
        }
        ll ans=0,w=0;
        for(ll i=0;i<s.size();i++)
        {
            if(s[i]=='0')w++;
            else w--;
            if((x-w)%p==0&&(x-w)/p>=0)ans++;
        }
        if(x==0)ans++;
        cout<<ans<<endl;
    }
    return 0;
}

 

C. Obtain The String

题意:

给定串sstt,长度范围均为1e51e5
你有一个空串,你想要构造串tt
每次操作时,你可以从ss中选择一个子序列,加到你的串中。
问你需要多少次操作,可以构造出串tt
构造不出则输出1-1
 

思路:

首先,构造不出的情况可以直接判掉,即ss中不存在tt中的某个字母。
我们设一个下标表示ss中当前在找到的位置。
然后去遍历tt的每一个字母,要在ss当前找的位置后面快速找到这个字母。
然后只要预处理出ss中每一个位置上,每个字母的下一个位置,就可以快速找到那个字母了,总体复杂度o(26×n)o(26×n)
不太好讲清楚,具体看代码吧。
 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
 
string s,t;
int dp[100005][26];
int has[26];
int main()
{
    int T;
    cin>>T;
    while(T--)
    {
        cin>>s>>t;
        for(int i=0;i<26;i++)
        {
            has[i]=0;
            dp[s.size()][i]=-1;
            for(int j=s.size()-1;j>=0;j--)
            {
                has[s[j]-'a']=1;
                if(s[j]=='a'+i)
                {
                    dp[j][i]=j;
                }
                else dp[j][i]=dp[j+1][i];
            }
        }
        int times=1,cur=0,ok=1;
        for(int i=0;i<t.size();i++)
        {
            if(has[t[i]-'a']==0)
            {
                ok=0;break;
            }
            cur=dp[cur][t[i]-'a'];
            if(cur==-1)
            {
                times++;
                cur=dp[0][t[i]-'a']+1;
            }
            else
            {
                cur=dp[cur][t[i]-'a']+1;
            }
        }
        if(ok==0)cout<<-1<<endl;
        else cout<<times<<endl;
    }
    return 0;
}

 
 

D. Same GCDs

题意:

给定两个数aamm(1a<m1010)(1\leqslant a<m\leqslant10^{10})
问你有多少个xx满足0x<m0\leqslant x<m,且gcd(a,m)=gcd(a+x,m)gcd(a,m)=gcd(a+x,m)
 

思路:

先初看下样例,发现当gcd(a,m)=1gcd(a,m)=1时,答案就是φ(m)\varphi(m),而且数据范围是1e101e10o(m)o(\sqrt{m})的复杂度也很舒适。
然后开始推式子,很容易让人想到辗转相除法,所以这个aa和这个a+xa+x应该放一起。于是就有了gcd(a,a+x)=gcd(a,x)=gcd(a,m)gcd(a,a+x)=gcd(a,x)=gcd(a,m)
这样还不好,再改成gcd(m,x)=gcd(a,m)gcd(m,x)=gcd(a,m)
gcdgcd除下来,变成gcd(mgcd(a,m),xgcd(a,m))=1gcd(\frac{m}{gcd(a,m)},\frac{x}{gcd(a,m)})=1
条件0x<m0\leqslant x<m等价于0xgcd(a,m)<mgcd(a,m)0\leqslant \frac{x}{gcd(a,m)}<\frac{m}{gcd(a,m)}
所以最后答案就是mgcd(a,m)\frac{m}{gcd(a,m)}的欧拉函数值,φ(mgcd(a,m))\varphi(\frac{m}{gcd(a,m)})
 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
 
ll getPhi(ll n)
{
    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);
    return ans;
}
 
ll gcd(ll a,ll b)
{
    return b?gcd(b,a%b):a;
}
 
int main()
{
    ll T;
    scanf("%lld",&T);
    while(T--)
    {
        ll a,m;
        scanf("%lld%lld",&a,&m);
        ll w=gcd(a,m);
        ll ans=getPhi(m/w);
        printf("%lld\n",ans);
    }
    return 0;
}

 
 

E. Permutation Separation

想出来有点晚,再给点时间就过了。

题意:

给定一个11nn的排列p1,p2pnp_{1},p_{2}…p_{n},还有移动每个位置需要的花费aia_{i}
(2n2×105,1ai2×109)(2\leqslant n\leqslant2\times 10^{5},1\leqslant a_{i}\leqslant2\times 10^{9})
现要求你将其划分成左右两个非空集合,即p1p2pkp_{1}p_{2}…p_{k}pk+1pk+2pnp_{k+1}p_{k+2}…p_{n}
然后要选择一些左边的元素移到右边去,再选择一些右边的元素移到左边去,最后使得右边的任意元素大于左边的任意元素,其中一边为空也可。
移动元素是要花费的,就是aia_{i}
现问你最少需要多少花费,才能满足条件。
 

思路:

想了挺久的。
首首先,我们的答案在最左边和最右边取个最小值。
首先,既然花钱移动了,那就没有影响了,所以不然改成删去,效果是一样的。
然后我们要确定一个方案的话,是有两个东西要去枚举的。
一个是分割的边界,即划分的集合边界。
一个是分割的数字,即左边最大数字和右边最小数字有个边界。换句话说,左边比这个大的都要删掉,右边比这个小的都要删掉。
 
显然暴力枚举这两个的复杂度是不够的,那么我们考虑枚举其中一个,然后快速求解另一个。
然后会发现另一个也不满足二分三分性质。
直到我在草稿本上画出了这张图。
在这里插入图片描述
这张图其实给出了一个很好的计算答案的方案。
上面一排的数字表示划分在左边集合的数字。
下面一排的数字表示划分在右边集合的数字。
然后我们要枚举的是1122之间的空隙,3344之间的空隙……计算其中最小的答案。而对于每一个空隙的计算就是,上面那排在空隙右边的数字的花费和加上下面这排在空隙左边的数字的花费和。
然后对于所有的空隙,我们要求一个最小值。
所以要用线段树来维护这些空隙,并且更新也是区间进行的。
 
我们再观察怎么更新。
最初时左边集合只有11个元素,右边是n1n-1个元素。
然后我们考虑将一个元素从右边移动到了左边。
比如33从右边集合移到了左边集合。我们发现33在右边集合时,其左边的空隙都会加上a3a_{3},现在从右边集合拿走了,所以再给他减掉。而移到左边集合后,就给33右边的空隙都加上a3a_{3}
 
至此,就是一个线段树区间更新,最值查询的模板题了。
 

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
 
ll tree[800005],laz[800005];
 
void push_down(ll k,ll l,ll r)
{
    if(l==r)
    {
        tree[k]=tree[k]+laz[k];
        laz[k]=0;
        return ;
    }
    laz[2*k+1]=laz[2*k+1]+laz[k];
    laz[2*k+2]=laz[2*k+2]+laz[k];
    tree[k]=tree[k]+laz[k];
    laz[k]=0;
}
 
void update(ll k,ll l,ll r,ll x,ll y,ll a)
{
    push_down(k,l,r);
    if(x<=l&&r<=y)
    {
        laz[k]=laz[k]+a;
        return ;
    }
    ll mid=(l+r)/2;
    if(x<=mid)update(2*k+1,l,mid,x,y,a);
    if(y>=mid+1)update(2*k+2,mid+1,r,x,y,a);
    push_down(2*k+1,l,mid);
    push_down(2*k+2,mid+1,r);
    tree[k]=min(tree[2*k+1],tree[2*k+2]);
}
 
ll query(ll k,ll l,ll r,ll x,ll y)
{
    push_down(k,l,r);
    if(x<=l&&r<=y)
    {
        return tree[k];
    }
    ll ans=1e18,mid=(l+r)/2;
    if(x<=mid)ans=min(ans,query(2*k+1,l,mid,x,y));
    if(y>=mid+1)ans=min(ans,query(2*k+2,mid+1,r,x,y));
    return ans;
}
ll p[200005];
ll a[200005];
int main()
{
    ll n;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)scanf("%lld",&p[i]);
    for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
    ll ans=min(a[1],a[n]);
    if(p[1]!=1)update(0,1,n-1,1,p[1]-1,a[1]);
    for(ll i=2;i<=n;i++)if(p[i]!=n)update(0,1,n-1,p[i],n-1,a[i]);
    ans=min(ans,query(0,1,n-1,1,n-1));
    for(ll i=2;i<=n-1;i++)
    {
        if(p[i]!=n)update(0,1,n-1,p[i],n-1,-a[i]);
        if(p[i]!=1)update(0,1,n-1,1,p[i]-1,a[i]);
        ans=min(ans,query(0,1,n-1,1,n-1));
    }
    printf("%lld\n",ans);
    return 0;
}

 
 

F. Good Contest

看都没看。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!