2024初三年前集训测试3

2024初三年前集训测试3

\(T1\) 夕景昨日 \(90pts\)

  • 部分分

    • \(10pts\) :输出 No
    • \(20pts\)\(O(2^{n})\)\(DFS\) 暴力枚举能得到的所有数,用 map 里进行判断。
    • \(90pts\) :输出 Yes
  • 正解

    • 观察到 \(1 \le n \le 100000,0 \le a_{i} \le 500000\)
    • 猜测 \(n\) 到达一定数量级时,一定有解。
    • \(n\) 个数最多产生 \(2^{n}\) 个数, \(a_{i}\) 最多配对出 \(nV\) 个数。经打表,枚举 \(V=1 \sim 500000\) 解关于 \(n\) 的不等式 \(2nV \le 2^{n}\) ,解得当 \(V=500000\) 时,\(n\) 的最小整数解为 \(25\)
      • 严格意义上来说,极限数据应该造成 \(1,2,4,8,16,32, \dots\)
    • 故当 \(n \le 25\) 时,仍选择用 \(DFS\) 暴力枚举;否则,一定有解。
    点击查看代码
    ll a[100001],flag=0;
    map<ll,ll>vis;
    void dfs(ll x,ll now,ll n)
    {
        if(x==n)
        {
            vis[now]++;
            if(vis[now]==2)
            {
                flag=1;
            }
        }
        else
        {
            dfs(x+1,now+a[x+1],n);
            dfs(x+1,now-a[x+1],n);
        }
    }
    int main()
    {
        ll n,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>a[i];
        }
        if(n<=25)
        {
            dfs(0,0,n);
            if(flag==0)
            {
                cout<<"No"<<endl;
            }
            else
            {
                cout<<"Yes"<<endl;
            }
        }
        else
        {
            cout<<"Yes"<<endl;
        }
        return 0;
    }
    

\(T2\) 透明答案 \(70pts\)

  • 部分分
    • \(30pts\) :输出 Bob
    • \(70pts\) :输出 Ayano
  • 正解
    • 博弈论
      • 弱化

        • 删去“此外,每 \(3\) 回合他们会添加一堆石子(含 \(2\) 块石子)。换句话说,在第 \(t\) 次操作(两个人操作的总次数)之后,如果可以被 \(3\) 整除,则添加一堆 \(2\) 块石子的石子堆。(即使在第 \(t\) 次操作中取完了所有石子,如果 \(t\) 可被 \(3\) 整除,也会添加新石子堆并继续游戏。)”,其他部分不变。
        • \(NIM\) 博弈,有 Ayano 获胜当且仅当 \(\bigoplus\limits_{i=1}^{n}a_{i}=0\) 时,即 \(n \equiv 0 \pmod{2}\) ;否则,有 Bob 获胜。
      • 手动模拟

        • \(n=1\) 时,有 Ayano 获胜。
        • \(n=2\) 时,有 Bob 获胜。
        • \(n=3\) 时,有 Ayano 获胜。
      • 推广

        • \(n\) 拆成 \(n=\left\lfloor \dfrac{n}{3} \right\rfloor \times 3+n \bmod 3\) 。前面 \(\left\lfloor \dfrac{n}{3} \right\rfloor \times 3\) 堆答案不造成影响,对答案造成影响的只有 \(n \bmod 3\) 。故当 \(n \equiv 2 \pmod{3}\) 时,有 Bob 获胜。 否则,有 Ayano 获胜。
        点击查看代码
        int main()
        {
            int n;
            cin>>n;
            if(n%3==2)
            {
                cout<<"Bob"<<endl;
            }
            else
            {
                cout<<"Ayano"<<endl;
            }
            return 0;
        }
        
    • \(DFS\)
      • 出题人称因是普及难度的比赛,所以把 \(DFS\) 放过去了。

\(T3\) 界外科学 \(50pts\)

  • 部分分

    • \(50pts\) :超大背包 \(O(2^{n})\) 枚举。

      点击查看代码
      ll a[50],b[50],ans=0;
      void dfs(ll x,ll worth,ll now,ll n,ll m)
      {
          if(x==n)
          {
              ans=(worth<=m)?max(ans,now):ans;           
          }
          else
          {
              dfs(x+1,worth^a[x+1],now+b[x+1],n,m);
              dfs(x+1,worth,now,n,m);
          }
      }
      int main()
      {
          ll n,m,i;
          cin>>n>>m;
          for(i=1;i<=n;i++)
          {
              cin>>a[i];
          }
          for(i=1;i<=n;i++)
          {
              cin>>b[i];
          }
          dfs(0,0,0,n,m);
          cout<<ans<<endl;
          return 0;
      }
      
    • \(100pts\)

      • 输出 \(\sum\limits_{i=1}^{n}b_{i}\)

      • 超大背包 \(O(2^{n})\) 的玄学优化,优化后时间复杂度未知。

        点击查看代码
        ll a[50],b[50],sum[50],ans=0;
        void dfs(ll x,ll worth,ll now,ll n,ll m)
        {
            if(x==n)
            {
                ans=(worth<=m)?max(ans,now):ans;           
            }
            else
            {
                if(now+sum[n]-sum[x]>ans)
                {
                    dfs(x+1,worth^a[x+1],now+b[x+1],n,m);
                }
                if(now+sum[n]-sum[x+1]>ans)
                {
                    dfs(x+1,worth,now,n,m);
                }
            }
        }
        int main()
        {
            ll n,m,i;
            cin>>n>>m;
            for(i=1;i<=n;i++)
            {
                cin>>a[i];
            }
            for(i=1;i<=n;i++)
            {
                cin>>b[i];
                sum[i]=sum[i-1]+(b[i]>0)*b[i];
            }
            dfs(0,0,0,n,m);
            cout<<ans<<endl;
            return 0;
        }
        
      • 面向数据点分治,当 \(n\) 较大时,选择 \(nV\)\(01\) 背包做法。

        • 这里背包不能滚成一维。
  • 正解

    点击查看官方题解

\(T4\) 回忆补时 \(30pts\)

  • 部分分

    • \(30pts\)\(O(n^{2}q)\) 暴力枚举。

      点击查看代码
      ll k[100001],b[100001];
      int main()
      {
          ll n,q,x,ans,i,j,h;
          scanf("%lld",&n);
          for(i=1;i<=n;i++)
          {
              scanf("%lld%lld",&k[i],&b[i]);
          }
          scanf("%lld",&q);
          for(i=1;i<=q;i++)
          {
              scanf("%lld",&x);
              ans=0;
              for(j=1;j<=n;j++)
              {
                  for(h=j+1;h<=n;h++)
                  {
                      ans=max(ans,max(k[h]*(k[j]*x+b[j])+b[h],k[j]*(k[h]*x+b[h])+b[j]));
                  }
              }
              printf("%lld\n",ans);
          }
          return 0;
      }
      
    • \(60pts\) :当 \(k_{j} \ge 0\) 时,\(k_{i}x+b_{i}\) 越大对答案的贡献越大;当 \(k_{j}<0\) 时,\(k_{i}x+b_{i}\) 越小对答案的贡献越小。考虑计算出 \(k_{i}x+b_{i}\) 的最大值、次大值、最小值、次小值,然后进行转移。

      点击查看代码
      ll k[100001],b[100001];
      int main()
      {
      	ll n,q,x,ans,zmax,cmax,zmin,cmin,idzmax,idzmin,i,j;
      	cin>>n;
      	for(i=1;i<=n;i++)
      	{
      		cin>>k[i]>>b[i];
      	}
      	cin>>q;
      	for(i=1;i<=q;i++)
      	{
      		cin>>x;
      		ans=zmax=cmax=idzmax=idzmin=0;
      		zmin=cmin=0x7f7f7f7f;
      		for(j=1;j<=n;j++)
      		{
      			if(k[j]*x+b[j]>zmax)
      			{
      				cmax=zmax;
      				zmax=k[j]*x+b[j];
      				idzmax=j;
      			}
      			else
      			{
      				cmax=max(cmax,k[j]*x+b[j]);
      			}
      			if(k[j]*x+b[j]<zmin)
      			{
      				cmin=zmin;
      				zmin=k[j]*x+b[j];
      				idzmin=j;
      			}
      			else
      			{
      				cmin=min(cmin,k[j]*x+b[j]);
      			}
      		}
      		for(j=1;j<=n;j++)
      		{
      			if(k[j]>=0)
      			{
      				ans=max(ans,(j==idzmax)?cmax*k[j]+b[j]:zmax*k[j]+b[j]);
      			}
      			else
      			{
      				ans=max(ans,(j==idzmin)?cmin*k[j]+b[j]:zmin*k[j]+b[j]);
      			}
      		}
      		cout<<ans<<endl;
      	}
      	return 0;
      }
      
  • 正解

    点击查看官方题解

总结

  • 打到 \(8:40\) 就去打矩阵快速幂了。
  • 要学会通过值域猜测时间复杂度和判断答案范围。

后记

  • 普及模拟赛考博弈论、折半搜索、李超线段树,难评。
  • 没有大样例,差评。
  • 建议本场比赛改名为暴力骗分/良心送分模拟赛。
posted @ 2024-02-05 16:51  hzoi_Shadow  阅读(52)  评论(5编辑  收藏  举报
扩大
缩小