2024 CSP-S 第二轮

2024.10.28 先拿 luogu ,云斗学院,信友队的民间数据测了测,不保证赛后复原代码正确性,等发了代码后会挂赛时代码,被 \(hack\) 了@我。

2024.11.04 换成赛时代码了。

\(T1\) luogu P11231 [CSP-S 2024] 决斗

  • 多倍经验: CF903C Boxes Packing

  • 将题面转化为退出游戏的怪兽尽可能多。

  • 攻击策略是优先攻击已经攻击过的怪兽(已经发挥作用了)。

  • 赛时想到的一个容易理解的思路是将所有数的出现次数升序排序后并抽象作柱状图,那么每一次“极长段”回合一定是参与攻击怪兽种类逐层递减且连续的一段,这一次“极长段”回合剩下的怪兽数量也是固定的,故众数出现次数即为所求。

    点击查看赛时代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define sort stable_sort
    #define endl '\n'
    int r[100010],cnt[100010];
    int main()
    {
        freopen("duel.in","r",stdin);
        freopen("duel.out","w",stdout);
        int n,i;
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&r[i]);
            cnt[r[i]]++;
        }
        sort(cnt+1,cnt+1+100000);
        printf("%d\n",cnt[100000]);
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    
    点击查看代码
    int r[100010],cnt[100010];
    int main()
    {	
        int n,ans=0,i;
        cin>>n;
        for(i=1;i<=n;i++)
        {
            cin>>r[i];
            cnt[r[i]]++;
            ans=max(ans,cnt[r[i]]);
        }
        cout<<ans<<endl;
        return 0;
    }
    

\(T2\) luogu P11232 [CSP-S 2024] 超速检测

  • 每辆车被判定超速都有一个区间,二分且判断边界即可,精度问题挑个好用的公式即可避免。

  • 第二问转化为给定若干个闭区间,在数轴上选尽量少的点使每个区间内至少有一个点(区间选点问题)。贪心策略比较典,例如 NOIP2024模拟2 T1 GHzoj 3731. 酸碱度中和 | NOIP2024模拟3 T2 GHzoj 3766. 核酸检测 | UVA10382 Watering Grass

    点击查看赛时代码
    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define ull unsigned long long
    #define sort stable_sort
    #define endl '\n'
    struct node
    {
        ll l,r;
    }e[1000010];
    ll d[100010],v[100010],a[100010],p[100010],cnt;
    double tmp[100010];
    void add(ll l,ll r)
    {
        cnt++;
        e[cnt].l=l;
        e[cnt].r=r;
    }
    bool cmp(node a,node b)
    {
        return (a.l==b.l)?(a.r<b.r):(a.l<b.l);
    }
    ll ask()
    {
        ll ans=0,last=0;
        sort(e+1,e+1+cnt,cmp);
        for(ll i=cnt;i>=1;i--)
        {
            if(e[i].l<=last&&last<=e[i].r)
            {
                continue;
            }
            else
            {
                last=e[i].l;
                ans++;
            }
        }
        return ans;
    }
    int main()
    {
        freopen("detect.in","r",stdin);
        freopen("detect.out","w",stdout);
        ll t,n,m,l,maxv,pos,ed,i,j;
        double s;
        scanf("%lld",&t);
        for(j=1;j<=t;j++)
        {
            cnt=0;
            scanf("%lld%lld%lld%lld",&n,&m,&l,&maxv);
            for(i=1;i<=n;i++)
            {
                scanf("%lld%lld%lld",&d[i],&v[i],&a[i]);
            }
            for(i=1;i<=m;i++)
            {
                scanf("%lld",&p[i]);
                tmp[i]=p[i];
            }
            for(i=1;i<=n;i++)
            {
                pos=lower_bound(p+1,p+1+m,d[i])-p;
                if(pos<=m)
                {
                    if(a[i]>0)
                    {
                        if(v[i]<=maxv)
                        {
                            s=1.0*(maxv*maxv-v[i]*v[i])/a[i]/2.0;
                            ed=upper_bound(tmp+1,tmp+1+m,1.0*d[i]+s)-tmp;
                            if(ed<=m)
                            {
                                add(ed,m);
                            }
                        }
                        else
                        {
                            add(pos,m);
                        }
                    }
                    if(a[i]==0)
                    {
                        if(v[i]>maxv)
                        {
                            add(pos,m);
                        }
                    }
                    if(a[i]<0)
                    {
                        if(v[i]>maxv)
                        {
                            s=1.0*(maxv*maxv-v[i]*v[i])/a[i]/2.0;
                            ed=lower_bound(tmp+1,tmp+1+m,1.0*d[i]+s)-tmp;
                            if(pos<=ed-1)
                            {
                                add(pos,ed-1);
                            }
                        }
                    }
                }
            }
            printf("%lld %lld\n",cnt,m-ask());
        }
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    
    点击查看代码
    pair<int,int>e[100010];
    int d[100010],v[100010],a[100010],p[100010],cnt=0;
    double tmp[100010];
    void add(int l,int r)
    {
        cnt++;
        e[cnt]=make_pair(l,r);
    }
    int ask()
    {
        int ans=0,last=0;
        sort(e+1,e+1+cnt);
        for(int i=cnt;i>=1;i--)
        {
            if(e[i].first<=last&&last<=e[i].second)
            {
                continue;
            }
            else
            {
                ans++;
                last=e[i].first;
            }
        }
        return ans;
    }
    int main()
    {
        int t,n,m,l,maxv,pos,ed,i,j;
        double s;
        cin>>t;
        for(j=1;j<=t;j++)
        {
            cnt=0;
            cin>>n>>m>>l>>maxv;
            for(i=1;i<=n;i++)
            {
                cin>>d[i]>>v[i]>>a[i];
            }
            for(i=1;i<=m;i++)
            {
                cin>>p[i];
                tmp[i]=p[i];
            }
            for(i=1;i<=n;i++)
            {
                pos=lower_bound(p+1,p+1+m,d[i])-p;
                if(pos<=m)
                {
                    if(a[i]>0)
                    {
                        if(v[i]<=maxv)
                        {
                            s=1.0*(maxv*maxv-v[i]*v[i])/2/a[i];
                            ed=upper_bound(tmp+1,tmp+1+m,1.0*d[i]+s)-tmp;
                            if(ed<=m)
                            {
                                add(ed,m);
                            }
                        }
                        else
                        {
                            add(pos,m);
                        }
                    }
                    if(a[i]==0)
                    {	
                        if(v[i]>maxv)
                        {
                            add(pos,m);
                        }
                    }
                    if(a[i]<0)
                    {
                        if(v[i]>maxv)
                        {
                            s=1.0*(maxv*maxv-v[i]*v[i])/2/a[i];
                            ed=lower_bound(tmp+1,tmp+1+m,1.0*d[i]+s)-tmp-1;
                            if(pos<=ed)
                            {
                                add(pos,ed);
                            }
                        }
                    }
                }
            }
            cout<<cnt<<" "<<m-ask()<<endl;
        }
        return 0;
    }
    

\(T3\) luogu P11233 [CSP-S 2024] 染色

  • 多倍经验:CF1799D1 Hot Start Up (easy version) | CF1799D2 Hot Start Up (hard version)

  • 部分分

    • \(20pts\) :爆搜。
    • \(50pts\)
      • \(f_{i,j,k}\) 表示 \([1,i]\) 中最后一个红色的数是 \(a_{j}\) ,最后一个蓝色的数是 \(a_{k}\) 时的最大得分。

      • 又因为 \(j,k\) 只有且仅有一个数 \(=i\) ,可以简化状态为 \(f_{i,j,0/1}\) 时表示 \([1,i]\) 中当 \(a_{i}\) 是红色/蓝色且最后一个蓝色/红色的数是 \(a_{j}\) 时的最大得分。

      • 转移是比较显然的,分讨 \(i-1\) 的颜色即可,若觉得填表法太难写就写刷表法。

      • 最终,有 \(\max\limits_{i=0}^{n-1}\{ f_{n,i,0},f_{n,i,1} \}\) 即为所求。

        点击查看赛时代码
        #include<bits/stdc++.h>
        using namespace std;
        #define ll long long
        #define ull unsigned long long
        #define sort stable_sort
        #define endl '\n'
        ll a[200010],f[2][200010][2];
        int main()
        {
            freopen("color.in","r",stdin);
            freopen("color.out","w",stdout);
            ll t,n,ans,i,j,k,h;
            scanf("%lld",&t);
            for(h=1;h<=t;h++)
            {
                ans=0;
                scanf("%lld",&n);
                for(i=1;i<=n;i++)
                {
                    scanf("%lld",&a[i]);
                }
                memset(f,0,sizeof(f));
                for(i=1;i<=n;i++)
                {
                    for(j=0;j<=i-2;j++)
                    {
                        f[i&1][j][0]=f[(i-1)&1][j][0]+(a[i-1]==a[i])*a[i];
                        f[i&1][j][1]=f[(i-1)&1][j][1]+(a[i-1]==a[i])*a[i];
                    }
                    for(k=0;k<=i-2;k++)
                    {
                        f[i&1][i-1][0]=max(f[i&1][i-1][0],f[(i-1)&1][k][1]+(a[k]==a[i])*a[i]);
                        f[i&1][i-1][1]=max(f[i&1][i-1][1],f[(i-1)&1][k][0]+(a[k]==a[i])*a[i]);
                    }
                }
                for(i=1;i<=n-1;i++)//赛后 hack : i 应该从 0 开始枚举
                {
                    ans=max(ans,max(f[n&1][i][0],f[n&1][i][1]));
                }
                printf("%lld\n",ans);
            }
            fclose(stdin);
            fclose(stdout);
            return 0;
        }
        /*
        in:
        1
        5 
        1 1 1 1 1
        ans:
        4
        out:
        3
        */
        
        点击查看代码
        ll a[200010],f[2][200010][2];
        int main()
        {
        	ll t,n,ans=0,i,j,k,h;
        	cin>>t;
        	for(h=1;h<=t;h++)
        	{
        		ans=0;
        		cin>>n;
        		for(i=1;i<=n;i++)
        		{
        			cin>>a[i];
        		}
        		memset(f,0,sizeof(f));
        		for(i=1;i<=n;i++)
        		{
        			for(j=0;j<=i-1;j++)
        			{
        				f[i&1][j][0]=max(f[i&1][j][0],f[(i-1)&1][j][0]+(a[i-1]==a[i])*a[i]);//当 j 固定时,f 随 i 是单调的,所以不用清空为 0
        				f[i&1][j][1]=max(f[i&1][j][1],f[(i-1)&1][j][1]+(a[i-1]==a[i])*a[i]);
        			}
        			for(k=0;k<=i-2;k++)
        			{
        				f[i&1][i-1][0]=max(f[i&1][i-1][0],f[(i-1)&1][k][1]+(a[k]==a[i])*a[i]);
        				f[i&1][i-1][1]=max(f[i&1][i-1][1],f[(i-1)&1][k][0]+(a[k]==a[i])*a[i]);
        			}
        		}
        		for(i=0;i<=n-1;i++)
        		{
        			ans=max(ans,max(f[n&1][i][0],f[n&1][i][1]));
        		}
        		cout<<ans<<endl;
        	}
        	return 0;
        }
        
  • 正解

    • 仔细考虑会发现 \(f_{i,j,0}=f_{i,j,1}\) ,启发我们进一步简化状态为 \(f_{i,j}\) 表示 \([1,i]\) 中与 \(a_{i}\) 颜色不相同的最后一个数是 \(a_{j}\) 的最大得分。
    • 此时转移方程只剩下了 \(\begin{cases} f_{i,j}=f_{i-1,j}+[a_{i-1}=a_{i}] \times a_{i} & j \in [0,i-2] \\ f_{i,j}=\max(f_{i-1,j}+[a_{i-1}=a_{i}] \times a_{i},\max\limits_{k=0}^{i-2} \{ f_{i-1,k}+[a_{k}=a_{i}] \times a_{i} \}) & j=i-1 \end{cases}\)
    • 尝试简化 \(j=i-1\) 的式子,有 \(f_{i,j}=\max(f_{i-1,j},\max\limits_{k=0}^{i-2} \{ f_{i-1,k}\},\max\limits_{k=0}^{i-2}\{ [a_{k}=a_{i}] \times (f_{i-1,k}+a_{i}) \})\)
    • 维护全局加,全局 \(\max\) ,某一特定值 \(\max\) ,单点修改即可。
    点击查看代码
    ll a[200010],f[200010],g[1000010];
    int main()
    {
        ll t,n,ans,sum,i,j;
        cin>>t;
        for(j=1;j<=t;j++)
        {
            ans=sum=0;
            cin>>n;
            for(i=1;i<=n;i++)
            {
                cin>>a[i];
                sum+=(a[i-1]==a[i])*a[i];
            }
            memset(f,0,sizeof(f));
            memset(g,-0x3f,sizeof(g));
            for(i=1;i<=n;i++)
            {
                if(a[i]==a[i-1])
                {
                    f[i-1]=max(f[i-1],g[a[i]]);
                }
                else
                {
                    f[i-1]=max(f[i-1],max(ans,g[a[i]]+a[i]));
                }
                g[a[i-1]]=max(g[a[i-1]],f[i-1]);
                ans=max(ans,f[i-1]);
            }
            cout<<ans+sum<<endl;
        }
        return 0;
    }
    

\(T4\) luogu P11234 [CSP-S 2024] 擂台游戏

  • 先咕了,可能会补个部分分(?)
posted @ 2024-10-28 17:15  hzoi_Shadow  阅读(184)  评论(0编辑  收藏  举报
扩大
缩小