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";
}
}
浙公网安备 33010602011771号