I - Cloud Retainer's Game

传送门: I. Cloud Retainer's Game (codeforces.com)

题意:

在坐标轴上有2个边界:y=0和y=H。有n个质点(木板),m个宝物(硬币)。一个小球从(0,0)点开始移动方向为↗。

小球遇到木板或边界之后,y轴方向上的速度取反,即↗变成↘。

问删掉任意个质点之后,小球最多遇到多少个宝物。

附上题面下面的图片帮助理解:

解法

移动有两个方向:分别是↗方向和↘方向,表达式分别为 \((x+y)\%2H=m\)\((2H-x+y)\%2H=m\)

也就是说,对于特征值m的路线,可以通过木板反弹在这两个表达式切换,或者删掉木板保持表达式。

\(dp_i\)为特征值为i的线路中遇到的宝物的最大数量。

我们可以按照x的大小从大到小往起点方向dp。(因为起点是所有路线一定经过且唯一确定的,其他点不一定是经过的,所以\(dp_0\)一定是最大的,也即答案)

对于一个木板,我们可以算出经过这个木板的两个方向的线路的特征值 \((x+y)\%2H=m1\)\((2H-x+y)\%2H=m2\)。因为可以删或不删从而在两个方向任意选择,所以\(dp_{m1},dp_{m2}\)都应该取\(max(dp_{m1},dp_{m2})\)

对于一个宝物,我们可以算出经过这个宝物的两个方向的线路的特征值 \((x+y)\%2H=m1\)\((2H-x+y)\%2H=m2\)。两个线路的贡献的加一即可。

代码实现:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6+10;



signed main(){
    ios::sync_with_stdio(false);
    int T;cin>>T;
    while(T--){
        vector<array<int,3>>ve;
        map<int,int>ans;
        int H;cin>>H;
        int n;cin>>n;
        for(int i=1;i<=n;i++){
            int x,y;cin>>x>>y;
            ve.push_back({x,y,0});
        }
        int m;cin>>m;
        for(int i=1;i<=m;i++){
            int x,y;cin>>x>>y;
            ve.push_back({x,y,1});
        }
        sort(ve.begin(),ve.end(),[](array<int,3>x,array<int,3>y){
            return x[0]>y[0];
        });
        for(auto i:ve){
            int x=i[0],y=i[1];
            int a=(x+y)%(2*H),b=(2*H-y+x)%(2*H);
            if(i[2]){
                ans[a]++;
                ans[b]++;
            }
            else {
                ans[a]=ans[b]=max(ans[a],ans[b]);
            }
        }
        cout<<ans[0]<<"\n";
    }

}

posted on 2022-11-09 16:13  wtn135687  阅读(192)  评论(0)    收藏  举报