5月赛总结

Posted mch5201314

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5月赛总结相关的知识,希望对你有一定的参考价值。

首先还是有很多东西没学好,其实看了题解之后就知道怎么做了。
我去我怎么这么辣鸡?学不好,混子,不做点难题,是没有进步的。
第一题
就是给你四个数字看是否可以通过四则运算得到24

分析

这种明显就是暴力,可是我当时就觉得分类讨论太多。自己没有信心去做。
其实你想最后得到24的方式就是两个数的加减乘除之一。那你就暴力把每种运算和每种排列全部
枚举就完了,注意的是除法必须是整除求分母不为0,我怀疑我不会递归。递归可以省很多代码量。而且不容易出错。
辣鸡死了

代码

#include<bits/stdc++.h>
using namespace std;
int a[4];
bool flag=0;
void fun(int pre,int la,int cur){
    if(flag) return;
    if(cur==3){
        if(pre+la==24||pre-la==24||pre*la==24)
        flag=1;
        if(la!=0&&pre%la==0&&pre/la==24)
        flag=1;
        return;
    }
    fun(pre+la,a[cur+1],cur+1);
    fun(pre-la,a[cur+1],cur+1);
    fun(pre*la,a[cur+1],cur+1);
    if(la!=0&&pre%la==0)
    fun(pre/la,a[cur+1],cur+1);
     
    fun(pre,la+a[cur+1],cur+1);
    fun(pre,la-a[cur+1],cur+1);
    fun(pre,la*a[cur+1],cur+1);
    if(a[cur+1]!=0&&la%a[cur+1]==0)
    fun(pre,la/a[cur+1],cur+1);
}
int main(){
    flag=0;
    for(int i=0;i<4;i++)
    scanf("%d",&a[i]);
    sort(a,a+4);
    do{
        fun(a[0],a[1],1);
    }while(next_permutation(a,a+4)&&!flag);
    if(flag) puts("Yes");
    else puts("No");
    return 0;
}

第二题
现在有两种操作: 第一种操作对[l,r]区间内每幢房子塞入一个皮卡丘; 第二种操作对第l次到第r次的操作重复一遍。
问依次进行m次操作后每幢房子内皮卡丘的个数。数据保证l < r 且对于第二种操作所给区间一定是之前的操作,对于第一种操作r一定<=n。
初始房子为空。
问你最后每个房子的数量。

分析

这我刚开始想用线段树维护这个东西,发现难度很大,根本不行。后来我想到了用差分。但是后面的操作,会把前面的操作给重复。我不知道怎么处理这个东西。
最后题解是差分,不过需要两个差分。一个维护第i次操作进行了几次,一个维护某个位置加了多少次。然后操作的差分要从后面开始,因为后面会包含前面的操作
这题你需要两个差分,而且方向还相反,属实是个好题。后面在遇到不会就砍头。气死了。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int N=1e5+10;
ll a[N],b[N];
struct str{
    int op,l,r;
}d[N];
int main(){
    int T,n,m;
    scanf("%d",&T);
    while(T--){
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        scanf("%d%d",&n,&m);
        b[m+1]++;
        b[0]--;
        for(int i=1;i<=m;i++)
        scanf("%d%d%d",&d[i].op,&d[i].l,&d[i].r);
        for(int i=m;i>0;i--){
            b[i]=(b[i]+b[i+1])%mod;
            if(d[i].op==2){
                int l=d[i].l,r=d[i].r;
                b[r]=(b[r]+b[i])%mod;
                b[l-1]=(b[l-1]-b[i]+mod)%mod;
            }
            else{
                int l=d[i].l,r=d[i].r;
                a[l]=(a[l]+b[i])%mod;
                a[r+1]=(a[r+1]-b[i]+mod)%mod;
            }
        }
        for(int i=1;i<=n;i++){
            a[i]=(a[i-1]+a[i])%mod;
            if(i!=n) printf("%lld ",a[i]);
            else printf("%lld\\n",a[i]);
        }
    }
    return 0;
}

第七题
就是个快速幂,不过这个地方取模1e12,快速幂会超过long long.我是意识到这个问题,可是后面的时间都没有解决办法。
后来看别人的,在快速幂哪里用了 __int128 就可以了。这个是long long的两倍。这个东西我没用过,这次知道了
正解是快速幂+乘法优化,变成1e6进制的数
我没搞懂,得补了。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e12;
ll fp(ll x,ll y){
    __int128 ans=1;
    __int128 ba=x%mod;
    while(y){
        if(y&1) ans=ans*ba%mod;
        ba=(ba*ba)%mod;
        y>>=1;
    }
    return ans;
}
int main(){
   long double a,b,c;
   ll d,n;
   int q;
   scanf("%Lf%Lf%Lf",&a,&b,&c);
   ll x,y,z,p,t;
   x=(ll)(a); t=(ll)(b); p=(ll)(c);
   /*1.9 2.1 2.2
   1
   100000000000000000 100000000000000000*/
   if(b-t>0) y=t+1ll;
   else y=t;
   z=c+0.5;
   scanf("%d",&q);
   while(q--){
    scanf("%lld%lld",&d,&n);
    ll ba=fp(d,n);
    ll ans=(x%mod+y%mod+z%mod+ba)%mod;
    printf("%lld\\n",ans);
   }
    return 0;
}

第九题
题目描述
作为acm集训队一员的你,有一天拿到了你的历史训练时长记录表。你当然是想让你的训练时长看起来好看一些,所以你想调整这份记录表,使得训练时长最少的一天的时间在所有可能的调整方案中最大。你还有m分钟就梦醒了,时间紧迫,你每一分钟可以使得连续的w天时长记录多一个单位时间。注意,在梦中,你不需要考虑一天有多少的时间限制。 输出经过你的调整,最小的时长。(睡醒前的答案)

输入
多组数据,第一行为一个整数T,表示数据组数(T<=10) 接下来每组数据,开头为n,m,w(1<=w , n<=100000, 0<=m <= 1000000),表示有记录的历史训练天数 接下来一行n个数,表示这一天你的签到时长t(0<=t<=1000000)

输出
输出T行,每行代表一组的答案。

分析

二分啊,不过加了一个技巧而已,自己不会弄。就是差分检查。发现这次考这个差分,这确实是个好东西啊。
二分最低哪个,check是否可以在m次以内做到。如果当前小于check值就进行那么多次加,同时往右的连续w个都加,所以用差分标记。
这没想到就是平时没有做这方面的,用的少,不知道这种套路。该死,多刷题啊老哥。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+10;
const int M=2e6+10;
int add[N],a[N];
int n,m,w;
bool ok(int k){
    memset(add,0,sizeof(add));
    int res=m;
    for(int i=1;i<=n;i++)
    {
        add[i]+=add[i-1];
        int h=k-a[i]-add[i];
        if(h>0)
        {
            res-=h;
            add[i]+=h;
            add[i+w]-=h;
        }
    }
    return res>=0?1:0; 
}
int main(){
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    int t;
    cin>>t;
    while(t--){
        cin>>n>>m>>w;
        for(int i=1;i<=n;i++)
        cin>>a[i];
        int l=0,r=M;
        int ans;
        while(l<=r){
            int mid=(l+r)>>1;
            if(ok(mid)){
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        cout<<ans<<endl;
    }
    return 0;
}

第十题
题目描述
有一个只包含1和2的序列,试翻转一个区间,使得结果中非递减子序列最长。输出翻转后数列中非递减子序列的最长长度。

输入
第一行为数据组数T,每组数据包含两行,第一行为序列的长度,第二行为n个数,表示数列中的数。(T <=6 && n <= 2e5)

输出
每组数据输出一行,表示答案。
这题是个dp,就是没想到。刷题太少了。不会套路。
其实就是找出最长的1111222211112222,我们可以开dp[4],分别表示以红色标出的值为结尾的最长子序列。
因为这样中间哪个22221111反转才是最多的。果然是做题太少了
转移公式
技术图片

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int dp[5],s[N];
int main(){
    int t,n;
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        for(int i=1;i<=n;i++)
        cin>>s[i];
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            dp[1]=dp[1]+(s[i]==1);
            dp[2]=max(dp[2]+(s[i]==2),dp[1]);
            dp[3]=max(dp[3]+(s[i]==1),dp[2]);
            dp[4]=max(dp[4]+(s[i]==2),dp[3]);
        }
        cout<<dp[4]<<endl;
    }
    return 0;
}

后面的题用到后缀数组,我还不会,哪个东西可以解决很多字符串的问题
很厉害的。要学啊
总结就是做题少了,sb一个

以上是关于5月赛总结的主要内容,如果未能解决你的问题,请参考以下文章

牛客小白月赛2 总结

usaco月赛,2017.1总结

java ITSA第57次月赛问题5.作业调度问题

牛客白月赛5 题解 数学场

借安恒月赛web pop对象注入+反序列化字符逃逸深究其逃逸原理

借安恒月赛web pop对象注入+反序列化字符逃逸深究其逃逸原理