【Educational Codeforces Round 60 (Rated for Div. 2)】 A.B.C.D.E

前言

由于最近状态不好,所以怂的打小号,比赛还是很顺利的,A题3min1A,之后看B,8min1A,然后看C,发现二分一下就可以,之后写了5分钟写完,时间差不多15min,但是答案应该是r我输出的l,质疑了自己十分钟才找到这个bug,C题28min1A,之后看D,写了个n*m的组合数做法,发现想不到优化,之后想dp,得到递推式之后矩阵快速幂,整体还算顺利,只是码力这些天有些下降,75min1A.之后被E吓到了,赛后补完之后才发现没有这么难,只是这些天思维有些下降了,警醒自己应该训练啦!

biu_biubiu r a t i n g + = 163 rating+=163 rating+=163 1665->1828


A. Best Subsegment

题意

设x为一个序列中的最大值,找到给定序列中最长的x的长度。

做法

按题意模拟即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn];
int main()
{
    int n,x;
    scanf("%d",&n);
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        ans=max(ans,a[i]);
    }
    int maxx=0;
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(a[i]==ans)
        {
            cnt=0;
            while(i<=n&&a[i]==ans)
            {
                cnt++;
                i++;
            }
            maxx=max(maxx,cnt);
        }
    }
    printf("%d\n",maxx);
    return 0;
}


B. Emotes

题意

给你n个数,你要在这些数中每次选出一个数写下来,一共写出m个数,一个数不能连续写k次,求最中m个数的最大和。
2 ≤ n ≤ 2 ∗ 1 0 5 2 \leq n \leq 2*10^5 2n2105
1 ≤ m ≤ 2 ∗ 1 0 9 1 \leq m \leq 2*10^9 1m2109
做法

注意到m很大,我们选择的方案一定是k个最大值,1个次大值,k个最大值,1个次大值…

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5+5;
ll a[maxn];
int main()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    sort(a+1,a+1+n);
    ll ans=0;
    ans=1LL*(m/(k+1))*(1LL*a[n]*k+a[n-1]);
    ans=ans+1LL*(m%(k+1))*a[n];
    printf("%lld\n",ans);
    return 0;
}


C. Magic Ship

题意

有一艘船,每个单位时间可以选择向U,D,L,R四个方向移动一个单位长度或者在原地不动
并且船会被风向具有周期性的风往当前风的方向吹一个单位长度,现在给你船的起始位置和目标位置
求船最少需要多少单位时间能到达目的地。

做法

答案满足二分性,因为如果我们已经到达,就可以一直与风反向行驶,保持船不动,所以具有二分性。
我们只需要二分答案,并且O(n)进行check船是否可以到达即可。

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
char str[maxn];
ll u,l;
int n;
ll x_1,x_2,y_1,y_2;
ll abs_(ll x)
{
    if(x<0) return -x;
    return x;
}
bool check(ll mid)
{
    ll shang=mid/n*u;
    ll zuo=mid/n*l;
    int tmp=mid%n;
    for(int i=1;i<=tmp;i++)
    {
        if(str[i]=='U') shang++;
        if(str[i]=='D') shang--;
        if(str[i]=='L') zuo++;
        if(str[i]=='R') zuo--;
    }
    ll xx1=x_1-zuo;
    ll yy1=y_1+shang;
    if(abs_(xx1-x_2)+abs_(yy1-y_2)<=mid) return true;
    return false;
}
int main()
{
    scanf("%lld%lld%lld%lld",&x_1,&y_1,&x_2,&y_2);
    scanf("%d",&n);
    scanf("%s",str+1);
    for(int i=1;i<=n;i++)
    {
        if(str[i]=='U') u++;
        if(str[i]=='D') u--;
        if(str[i]=='L') l++;
        if(str[i]=='R') l--;
    }
    ll l=0,r=2000000000000000000,mid;
    while(l<=r)
    {
        mid=(l+r)/2;
        if(check(mid)) r=mid-1;
        else l=mid+1;
    }
    if(l==2000000000000000001) printf("-1\n");
    else printf("%lld\n",l);
    return 0;
}


D. Magic Gems

题意

长度为n的01串0的个数是m的倍数,而且每m个0都是连续的方案数。
1 ≤ n ≤ 1 0 18 1 \leq n \leq 10^{18} 1n1018
2 ≤ m ≤ 100 2 \leq m \leq 100 2m100
做法

可以看到n很大,我们首先想n很小的时候怎么做,我们用dp[i]表示长度为i的串的合法方案数,第i位放1的方案数为dp[i-1],第i位放0的方案数位dp[i-m],所以得到转移方程dp[i]=dp[i-m]+dp[i-1],由于m很小,很显然可以通过矩阵快速幂加速这个转移。
时间复杂度 O ( m 3 ∗ l o g ( n ) ) O(m^3*log(n)) O(m3log(n))

代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int Mod=1000000007;
void add(ll &x,ll y)
{
    x+=y;
    if(x>=Mod) x%=Mod;
}
struct mat
{
    ll jz[105][105];
};
int m;
mat mat_mul(mat x,mat y)
{
    mat res;
    memset(res.jz,0,sizeof(res.jz));
    for(int i=0;i<m;i++)
        for(int j=0;j<m;j++)
            for(int k=0;k<m;k++)
               add(res.jz[i][j],x.jz[i][k]*y.jz[k][j]);
        return res;
}
ll power_mod (ll P)//.res是系数矩阵,ans是变换矩阵!!左->ans,右->res.!!
{
    mat ans,res;
    for(int i=0;i<m;i++)
    {
        for(int j=0;j<m;j++)
        {
            ans.jz[i][j]=0;
            res.jz[i][j]=0;
        }
    }
    for(int i=0;i<=m-2;i++) res.jz[i+1][i]=1;
    res.jz[0][m-1]=1;
    res.jz[m-1][m-1]=1;
    for(int i=0;i<=m-1;i++) ans.jz[0][i]=1;
    while(P>0)
    {
        if(P&1)  ans=mat_mul(ans,res);//所以应该答案矩阵在左ans,res);
        P=P>>1;
        res=mat_mul(res,res);
    }
    return (ans.jz[0][m-1]+Mod)%Mod;//返回指定位置元素
}
int main()
{
   ll n;
   scanf("%lld%d",&n,&m);
   if(n<=m-1) printf("1\n");
   else printf("%lld\n",power_mod(n-m+1));
   return 0;
}


E. Decypher the String

题意

就是给你一个长度为n的字符串的转换规则,规则是最多可以交换任意两个位置n次,得到新的字符串,现在给你一个转换之后的字符串,让你求原来的字符串。
最多提问三次,每次提问你给出一个长度为n的字符串,交互会按照规则返回转换之后的字符串。

1 ≤ n ≤ 1 0 4 1 \leq n \leq 10^4 1n104
做法

这道题不考虑那个转换规则,我们只知道最后每个位置一定由之前字符串的某个位置而来,而且是一一对应的。所以我们只要知道每个字符之前在哪个位置就可以,我们第一次提问用一个
26 ∗ 26 ∗ a , 26 ∗ 26 ∗ b , 26 ∗ 26 ∗ c . . . . . 26 ∗ 26 ∗ z 26*26*a,26*26*b,26*26*c.....26*26*z 2626a,2626b,2626c.....2626z 这样的字符串提问,我们就知道每个字符来自哪一个块中,之后再用 26 ∗ a , 26 ∗ b , 26 ∗ c . . . . 26 ∗ z 26*a,26*b,26*c....26*z 26a,26b,26c....26z这样的字符串提问,就知道这个字符来自哪一个小块,因为之前确定大块,而每个大块只包含26种小块,所以可以根据答案判断这个位置来自哪个大块的哪个小块。同理最后用 a , b , c , d . . . z a,b,c,d...z a,b,c,d...z去提问,就知道每个字符来自哪个位置了,其实仔细想一下就是一个26进制。因为 2 6 3 &gt; 10000 26^3&gt;10000 263>10000 ,所以每个位置都有唯一的对应关系。
代码

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int maxn = 1e5+5;
char str[maxn],tmp[maxn];
char ans[3][maxn];
char rrs[3][maxn];
map<string,int> mp;
char ou[maxn];
int main()
{
    scanf("%s",tmp);
    int len=strlen(tmp);
    for(int i=0;i<len;i++)
    {
        ans[0][i]=char(i/(26*26)+'a');
        ans[1][i]=char((i%(26*26))/26+'a');
        ans[2][i]=char(i%26+'a');
        string res="";
        res+=ans[0][i];
        res+=ans[1][i];
        res+=ans[2][i];
        mp[res]=i;//i位置三次查询串的字符为res
    }
    for(int i=0;i<3;i++) ans[i][len]='\0';
    cout<<"?"<<" "<<ans[0]<<endl;
    scanf("%s",str);
    for(int i=0;i<len;i++) rrs[0][i]=str[i];
    cout<<"?"<<" "<<ans[1]<<endl;
    scanf("%s",str);
    for(int i=0;i<len;i++) rrs[1][i]=str[i];
    cout<<"?"<<" "<<ans[2]<<endl;
    scanf("%s",str);
    for(int i=0;i<len;i++) rrs[2][i]=str[i];
    for(int i=0;i<len;i++)
    {
        string res="";
        res+=rrs[0][i];
        res+=rrs[1][i];
        res+=rrs[2][i];
        ou[mp[res]]=tmp[i];
    }
    ou[len]='\0';
    cout<<"!"<<" "<<ou<<endl;
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值