首页 > 其他分享 >2024初三年后集训模拟测试4

2024初三年后集训模拟测试4

时间:2024-02-22 19:44:48浏览次数:31  
标签:int register long 2024 循环 define 集训 模拟 getchar

前言

image
普及模拟赛,但是分拿的不高,主要想 \(T1\) 想时间太长了,别的没时间做了,时间分配有问题。

  • \(T1~100pts:\)

    模拟+打表,立体的骰子不太容易想,规律也不好找,但发现规律后超级简单,我敢说我发现的规律是全机房最简便的。

    但是想的时间用太长了,已经做出来了还验证半天。

    题解写的详细写吧,好不容易想的。

  • \(T2~10pts:\)

    贪心打假了。

  • \(T3~0pts:\)

    赛时没时间了,赛后后悔死了,异常简单,所有数的欧拉函数乘起来就完事了。

  • \(T4~50pts:\)

    背包 \(DP\) 会炸,\(10pts\) ,改成暴力 \(dfs\) \(O(2^n)\) ,也会超时 \(50pts\) 。

    但是隔壁新任 剪枝教教主 @minecraft666 超级剪枝暴搜 \(100pts\) 。

    数据有多水?超时的点全是 \(=m\) ,赛后加个 \(=m\) 直接输出的剪枝就 \(100pts\) 了 \(QWQ\) 。

T1 打赌

  • 题意:

    直接看吧:

    image

    注意这 \(4\) 个操作是循环进行的,直到最后一个点。

    image

  • 解法:

    一眼大模拟,但是全交给电脑会 \(TLE\) ,\(n,m\leq 100000\) 。

    考虑打表(找规律):

    • \(O(n)\) 解法:

      显然在左右方向滚的时候,是 \(4\) 个一循环的,那么现在就是求对于每一行他的循环状态,设这个循环为 \(a_{1\sim 4}\)

      每 \(4\) 个为一循环,每个循环的和为 \(14\) ,这是显然的。

      不管如何,第一行肯定是 \(1,4,6,3,1,4……\) 循环的,要做的就是从当前行状态求出下一行状态。

      • 先说结论,请看图:

        image

        当前行最后处于上面的节点,也就是 \(a_{m\bmod 4}\) (视 \(0\) 作 \(4\) )这个点在当前循环的状态,在每一行的循环中,他的上下是固定的,也就是 \(5,2\) 这两个点。

        以 \(1\) 这个点为例,若最后处于这个点,则下一行的循环状态为其 上,左,下,右 循环;当前点为下一循环的 ,其 为 \(7\) 减去当前点(相对两点相加为 \(7\) ) 。

        设当前节点状态为 \(a_{now},c_1,c_2\) ,分别表示其循环、上和下;设下一层循环状态为 \(b_{1\sim 4},d_1,d_2\) 。

        则有 \(b_1=c_1,b_2=a_{now-1},b_3=c_2,b_4=a_{now+1},c_2=a_{now},c_1=7-a_{now}\) 。

        实际上就是该状态轮一圈,就是下一行的状态。

      • 解析其正确性:

        读题会发现在第 \(i\) 行时,若 \(i\) 为奇数是向右滚,反之向左滚,而前面却没有分类讨论。

        在仔细思考题面发现,当 \(i\) 为奇数时,他的循环状态排列和正常平面图左右是相反的,如下图:

        image

        因为他是向右滚的,所以上一个滚到的显然是他的右侧,下一个滚到的为其左侧。

        那么真实的情况就是:

        image

        想象一下这个立体情景,那么他下一层的状态就应该是:

        image

        这次他要向左滚了,下一个点是他右边的点,那么他的循环就是 \(5,3,2,4\) 。

        发现就成了我们上面的结论。

        这是处理完了 \(i\) 为奇数的,\(i\) 为偶数同理的,只是将左右反过来,这次左右不反了,也是上面的结论。

      • 最后实现:

        直接按照结论来即可,注意 \(4\bmod 4=0\) 的问题,可以将 \(a\) 开成 \(0\sim 4\) ,\(a_0=a_4\) 。

      点击查看代码
      #include<bits/stdc++.h>
      #define int long long 
      #define endl '\n'
      using namespace std;
      const int N=2e5+10;
      template<typename Tp> inline void read(Tp&x)
      {
          x=0;register bool z=1;
          register char c=getchar();
          for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
          for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
          x=(z?x:~x+1);
      }
      int n,m,a[5]={3,1,4,6,3},b[5],c[3]={0,5,2},x,ans;
      signed main()
      {
          // #ifndef ONLINE_JUDGE
          // freopen("in.txt","r",stdin);
          // freopen("out.txt","w",stdout);
          // #endif
          freopen("pogodak.in","r",stdin);
          freopen("pogodak.out","w",stdout);
          read(n),read(m);
          if(m%4==0) cout<<n*m/4*14,exit(0);
          int s=m/4,t=m%4;
          for(int i=1;i<=n;i++)
          {
              ans+=s*14;
              for(int j=1;j<=t;j++) ans+=a[j];
              if(t==0) x=4;
              else x=t;
              b[1]=c[1],b[3]=c[2];
              b[2]=a[x-1],b[0]=b[4]=a[(x+1)%4];
              c[2]=a[x],c[1]=7-a[x];
              for(int j=0;j<=4;j++) a[j]=b[j];
          }
          cout<<ans;
      }
      
    • \(O(1)\) 解法:

      打表

      他总共只有 \(m\bmod 4={0,1,2,3}\) 四种情况,为 \(0\) 时直接输出 \(n\times \dfrac{m}{4}\times 14\) 即可,就剩下三种。

      直接用三种情况把上面三种情况在本地搞出来,把这个表交上去就可以了。

      然后发现没什么意义,没比 \(O(n)\) 快多少甚至没有 \(O(n)\) 快,因为多处模运算常数巨大。

      极其简单的,主函数里有用部分就 \(1\) 行。

      点击查看代码
      #include<bits/stdc++.h>
      #define int long long 
      #define endl '\n'
      using namespace std;
      const int N=2e5+10;
      template<typename Tp> inline void read(Tp&x)
      {
          x=0;register bool z=1;
          register char c=getchar();
          for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
          for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
          x=(z?x:~x+1);
      }
      int n,m;
      const signed c[4]={1,4,6,2},
      cas[4][6]=
      {0,0,0,0,0,0,
      14,1,6,12,0,0,
      42,5,11,19,28,36,
      22,11,0,0,0,0};
      signed main()
      {
          #ifndef ONLINE_JUDGE
          freopen("in.txt","r",stdin);
          freopen("out.txt","w",stdout);
          #endif
          std::ios::sync_with_stdio(false);
          std::cin.tie(0);std::cout.tie(0);
          freopen("pogodak.in","r",stdin);
          freopen("pogodak.out","w",stdout);
          read(n),read(m);
          const signed ans=m%4;
          cout<<n*(m/4)*14+cas[ans][0]*(n/c[ans])+((n%c[ans]==0)?0:cas[ans][n%c[ans]]);
      }
      

T2 舞会

  • 题意:

    直接看吧:

    image

  • 部分分都是打假了的,没人尝试 \(O(n^2)\) ,没有整理的必要。

  • 正解:

    贪心

    分成四个数组,分别为 低(想要)男,高男,低女,高女 的身高。设为 \(a_1,a_2,b_1,b_2\) 。

    全部从小到大排序,\(a_1→b_2,a_2→b_1\) 分组。

    双指针贪心即可,尽可能选择离自己身高最接近的舞伴。

    时间复杂度 \(O(n)\) 。

    直接看代码理解吧,有注释:

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    using namespace std;
    const int N=1e5+10;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    int n,ans,girl1[N],girl2[N],boy1[N],boy2[N],cnt1,cnt2,tot1,tot2;
    bool v[N];
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        freopen("party.in","r",stdin);
        freopen("party.out","w",stdout);
        read(n);
        for(int i=1;i<=n;i++)
        {
            int x;
            read(x);
            if(x<0) boy1[++tot1]=abs(x);
            else boy2[++tot2]=abs(x);
        }
        for(int i=1;i<=n;i++)
        {
            int x;
            read(x);
            if(x<0) girl1[++cnt1]=abs(x);
            else girl2[++cnt2]=abs(x);
        }
        stable_sort(boy1+1,boy1+1+tot1);
        stable_sort(boy2+1,boy2+1+tot2);
        stable_sort(girl1+1,girl1+1+cnt1);
        stable_sort(girl2+1,girl2+1+cnt2);
        for(int i=1,j=1;i<=tot1,j<=cnt2;i++,j++)//这里 i 也要 ++,上一个选他了这个就不能选他了。
        {
            while(i<=tot1&&boy1[i]<=girl2[j])
                i++;
            ans+=(i<=tot1);//从上面循环中跳出来的,一定是第一个(最近的一个)能配对的,如果一直到最后一个都不符合,那么跳出来的就是 tot1+1。
        }
        for(int i=1,j=1;i<=tot2,j<=cnt1;i++,j++)
        {
            while(j<=cnt1&&boy2[i]>=girl1[j]) //同理,男女调换而已。
                j++;
            ans+=(j<=cnt1);
        }
        cout<<ans;
    }
    

T3 最小生成树

  • 题意:

    \(n\) 个点构成完全图,每两点 \(i,j\) 间有且仅有一条边,边权为 \(\gcd(i,j)\) 。

    现将其转换为最小生成树,要求父亲节点编号小于子节点,求方案数。

  • 部分解:

    \(40pts:\) 不开 \(long~long\) 。

  • 正解:

    欧拉函数

    首先,\(1\) 与任何数 \(\gcd\) 均为 \(1\) ,所以将所有其他点都连到 \(1\) 就构成了最小生成树的一种情况。

    那么由此,最小生成树要求所有边权为 \(1\) ,同时要求父亲节点小于子节点,恰好符合欧拉函数的定义。

    求 \(\prod\limits_{i=1}^n\varphi(i)\) 即可。

    可以线性筛出 \(1\sim n\) 的欧拉函数,时间复杂度 \(O(n)\) 。

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    using namespace std;
    const int N=2e4+10,P=1e8+7;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    int n,phi[N],ans,len,prime[N];
    bool vis[N];
    void euler(int n)
    {
        phi[1]=1;
        for(int i=2;i<=n;i++)
        {
            if(!vis[i])
                len++,
                prime[len]=i,
                phi[i]=i-1;
            for(int j=1;j<=len&&i*prime[j]<=n;j++)
            {
                vis[i*prime[j]]=1;
                if(!(i%prime[j]))
                {
                    phi[i*prime[j]]=phi[i]*prime[j];
                    break;
                }
                else phi[i*prime[j]]=phi[i]*(prime[j]-1);
            }
        }
    }
    signed main()
    {
        // #ifndef ONLINE_JUDGE
        // freopen("in.txt","r",stdin);
        // freopen("out.txt","w",stdout);
        // #endif
        freopen("mst.in","r",stdin);
        freopen("mst.out","w",stdout);
        ans=1;
        read(n);
        euler(n);
        for(int i=1;i<=n;i++) ans=ans*phi[i]%P;
        cout<<ans;
    }
    

T4 买汽水

  • 题意:

    已知 \(a_{1\sim n}\) 和 \(m\) ,选取若干个相加使其和不大于 \(m\) 的情况下最大,求最大和。

  • 部分分:

    • \(10pts:\)

      背包 \(DP\) ,但是会炸空间和时间,题面数据范围给的不对差评。

    • \(50pts/60pts/70pts:\)

      暴力 \(dfs\) 加剪枝,复杂度 \(O(2^n)\) ,正常是 \(50pts\) ,至于怎么超级剪枝请询问 @剪枝教教主

    • \(60pts:\)

      输出 \(m\) ,对你没看错。

    • \(100pts:\)

      歪解。

      暴力 \(dfs\) 加剪枝加 \(sum=m\) 时直接输出 \(m\) 。

      对,数据就是这么抽象。

  • 正解:

    双向搜索 ,又叫 折半搜索 ,官方名为 meet in middle

    顾名思义拆成 \(1\sim \dfrac{n}{2},\dfrac{n}{2}+1\sim n\) 两部分进行搜索,在让他们两个相遇。

    将第一遍搜索得到价值存到一个 set 里,第二遍搜索得到的价值在 set 里面找满足 \(\leq m-sum\) (当前数量)的最大价值,进行转移即可。

    这样就将复杂度优化为 \(O(2^{\frac{n}{2}}\log(n))\) 。

    点击查看代码
    #include<bits/stdc++.h>
    #define int long long 
    #define endl '\n'
    using namespace std;
    const int N=50;
    template<typename Tp> inline void read(Tp&x)
    {
        x=0;register bool z=1;
        register char c=getchar();
        for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
        for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
        x=(z?x:~x+1);
    }
    int n,m,sum,a[N],ans,f[N][2];
    set<int>vis;
    void dfs(int x,int sum)
    {
        if(x>n/2)
        {
            if(sum<=m) vis.insert(sum);
            return ;
        }
        if(sum==m) cout<<m,exit(0);
        if(sum>m) return ;
        ans=max(ans,sum);
        if(sum+a[x]<=m) dfs(x+1,sum+a[x]);
        dfs(x+1,sum);
    }
    void dfsr(int x,int sum)
    {
        if(x>n)
        {
            if(sum<=m) 
                ans=max(ans,sum+(*(--vis.upper_bound(m-sum))));
            return ;
        }
        if(sum==m) cout<<m,exit(0);
        if(sum>m) return ;
        ans=max(ans,sum);
        if(sum+a[x]<=m) dfsr(x+1,sum+a[x]);
        dfsr(x+1,sum);
    }
    signed main()
    {
        #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
        #endif
        freopen("drink.in","r",stdin);
        freopen("drink.out","w",stdout);
        read(n),read(m);
        for(int i=1;i<=n;i++) read(a[i]),sum+=a[i];
        if(sum<=m) cout<<sum,exit(0);
        dfs(1,0);
        dfsr(n/2+1,0);
        cout<<ans;
    }
    

总结:

了解新知识——双向搜索。

教训:合理分配时间。

开题顺序不一定要挨着来,本次 \(T4→T2→T3→T1\) 最好。

要学会深入分析题面,如 \(T3\) 。

继续增强了大模拟和打表的能力,立体空间想象能力。

标签:int,register,long,2024,循环,define,集训,模拟,getchar
From: https://www.cnblogs.com/Charlieljk/p/18027801

相关文章

  • 2024/2/22 还是要记录才算学了
    什么是Cmake?打个比喻,小明在路边卖煎饼赚了300万准备买房,但是小明这一麻袋的5毛、一块、十块、五十、一百售楼处的小姐姐嫌麻烦不想收这些钱,那怎么办呢?小姐姐建议小明把钱拿到银行去换成一张银行卡,然后直接来刷卡就行啦!CMake这里就扮演银行的角色。MinGW提供了一套简单方便的Win......
  • 龙哥盟 Python 译文集 2024.2 更新
    每个程序员都应该知道的40个算法Python数学应用Python入门指南Python物联网入门指南Python比特币编程实用指南Python密码学实用指南Python数据结构和算法实用指南Python企业自动化实用指南PythonGPU编程实用指南Python物联网项目LearningScrapy中文版通......
  • 2024初三集训模拟测试4
    打了一场模拟赛又没命了2024初三集训模拟测试4题目难度T4\(\le\)T2\(\le\)T3\(\le\)T1T1打赌非常好题目,使我骰子旋转定义三个变量记录当前状态:上,前,左横着旋转,四个一循环,\(ans\)直接加$14$(模拟模拟模拟模拟)模拟一下就可以码#include<bits/stdc++.h>......
  • 【2024.02.22】构图练习(滨田英明)
    ......
  • Jenkins CLI 任意文件读取漏洞(CVE-2024-23897)复现
    0x00漏洞简介Jenkins是一款基于JAVA开发的开源自动化服务器。Jenkins使用args4j来解析命令行输入,并支持通过HTTP、WebSocket等协议远程传入命令行参数。在args4j中,用户可以通过@字符来加载任意文件。这一特性存在安全风险,攻击者可以利用它来读取服务器上的任意文件。0x01影响......
  • 2024-02-21-物联网Shell语言(2-系统调用)
    2.系统调用2.1系统编程概述操作系统的职责:操作系统用来管理所有的资源,并将不同的设备和不同的程序关联起来Linux系统编程:在有操作系统打的环境下编程,并使用操作系统提供的系统调用及库函数,对系统资源进行访问系统编程就是为了让用户更方便的操作硬件设备,并且对硬件设备起到......
  • dp 学习笔记 (2024/2/22 - )
    计数[ARC107D]NumberofMultisets[ARC104D]MultisetMean大值域限制偏序计数[CF1295F]GoodContest[ARC104E]RandomLIS......
  • 2024初三集训模拟测试4
    2024初三集训模拟测试4\(T1\)打赌\(0pts\)\(T2\)舞会\(0pts\)\(T3\)最小生成树\(0pts\)经打表,有最小生成树的边权和为\(n-1\),构造每条边上的两端点互质即可。故\(\prod\limits_{i=1}^{n}\varphi(i)\)即为所求。点击查看代码constllp=100000007;llph......
  • 2024初三集训模拟测试4
    T1打赌简单题,模拟一下即可。T2舞会小贪心,尽量找离自己最近的防止后面的不能找。T3最小生成树显然权值和为\(n-1\),就是连互质的数,然后要求父亲小于儿子,所以欧拉函数一乘即可。T4买汽水正解是分成两组后搜索加剪枝,随机化也能过,数据很水。......
  • 2024.02《高效学习法》
     背口诀、划重点、反复温习……各种方法都试过了,可为什么学习成绩还是没有提高呢?其实,你不是不会学习,而是不知道正确的学习方法!日本学习之神DaiGo公开自己独创并长年使用、科学有效、实践性极高的学习秘籍:想象自己把学习内容输出到10岁的孩子都能听懂、不写学习目标而写你掌......