题目大意:有N个塔,水平坐标为 p[i], 高度为 h[i], 每个塔的水平坐标各不相同。 有K个气球,每个气球可看做一个点,坐标为x[i], y[i]。一个人可以爬到每个塔的任意高度位置,然后在该位置可以向左右45度滑行,滑行的轨迹是直线。如果在途中遇到气球,则可获得该气球,如果气球和塔相重叠,也认为可获得气球,要求一共可以获得多少个气球。
解题关键点:
1. 如果从一个塔( p[j], h[j] )向下滑行可以获得某个气球(x[i], y[i]), 则满足 abs(x[i] - p[j] ) + y[i] <= h[j]
2. 如果一个塔 i 和另外一个塔 j 满足 abs(p[i] - p[j]) + h[i] <= h[j], 则第i个塔是多余的,因为任意从第i个塔滑行获得的气球,都可以通过从第j个塔滑行获得。
解题方法:
1. 小数据: 将每个气球和所有的塔进行关键点1条件比较,如果满足,则该气球可获得。
2. 大数据: 对于每个气球,如果我们能找到能它最近的塔,只判断是否能从离它最近的塔上获得就好了。但是存在这样一个问题,如果从离它最近的塔上不能获得,也许存在离它较远位置的塔,从这些塔上获得气球。这样的话,还得依次遍历所有的塔。为了达到最初的想法,考虑关键点2, 如果我们把所有多余的塔去掉,那么就可以只判断是否能从离它最近的塔上获得就可以了。为什么? 假设某个气球坐标为(x, y) , 离它最近的右边的塔为(p1, h1),从该塔不能获得气球,则满足 p1 - x + y > h1, 该式子等于 -p1 + x - y + h1 < 0, 又假设存在离该气球更远的塔(p2, h2 ) p2 >p1, 从该塔上可以获得气球,则满足 p2 - x + y <= h2, 将这个式子和前面的式子相加得到 p2 - p1 + h1 <= h2,正好关键点2中的条件,也就是说离它最近的那个塔是多余的。 所以,我们把所有多余的塔去掉之后,就可以找到离某个气球最近的塔,找的过程可以将所有的塔从小到大排好序,再使用二分搜索进行查找。
代码:
#include <iostream>
#include <iostream>
#include <string.h>
#include <iomanip>
#include <algorithm>
#include <stdio.h>
#include <map>
#include <set>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int maxn = 100010;
ll p[maxn], h[maxn], x[maxn], y[maxn];
ll relevantT[maxn];
ll towerSorted[maxn];//排好序的塔
map<ll, int> valueAndID; //塔的位置和索引
ll A[5], B[5], C[5], M[5];
int T, N, K;
int getRelation(int i, int j)//第i个塔, 第j个塔 i > j
{
if(h[j] >= p[i] - p[j] + h[i]) //第i个塔是多余的
return 1;
if(h[i] >= p[i] - p[j] + h[j]) //第j个塔是多余的
return 2;
return 3;
}
bool ok(int i, int j)//第i个气球,第j个塔
{
if(abs(x[i] - p[j]) + y[i] <= h[j])
return true;
return false;
}
int getRelevantTowers()
{
for(int i = 1; i <= N; i++)
{
towerSorted[i] = p[i];
valueAndID[p[i]] = i;
}
sort(towerSorted + 1, towerSorted + 1 + N);
stack<ll> st;
for(int i = 1; i <= N; i++)
{
if(st.empty())
{
st.push(towerSorted[i]);
continue;
}
int relation = getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]);
if(relation == 1)//多余的
continue;
if(relation == 2)//栈顶的塔是多余的
{
st.pop();
while(!st.empty() && getRelation(valueAndID[towerSorted[i]], valueAndID[st.top()]) == 2)
{
st.pop();
}
}
st.push(towerSorted[i]);
}
int len = st.size();
int tp = len;
while(!st.empty())
{
relevantT[tp--] = st.top();
st.pop();
}
return len;
}
int main()
{
freopen("B-large-practice.in", "r", stdin);
freopen("out2.txt", "w", stdout);
cin >> T;
for(int cas = 1; cas <= T; cas++)
{
valueAndID.clear();
cin >> N >> K;
cin >> p[1] >> p[2] >> A[1] >> B[1] >> C[1] >> M[1];
for(int i = 3; i <= N; i++)
p[i] = (A[1] * p[i - 1] + B[1] * p[i - 2] + C[1]) % M[1] + 1;
cin >> h[1] >> h[2] >> A[2] >> B[2] >> C[2] >> M[2];
for(int i = 3; i <= N; i++)
h[i] = (A[2] * h[i - 1] + B[2] * h[i - 2] + C[2]) % M[2] + 1;
cin >> x[1] >> x[2] >> A[3] >> B[3] >> C[3] >> M[3];
for(int i = 3; i <= K; i++)
x[i] = (A[3] * x[i - 1] + B[3] * x[i - 2] + C[3]) % M[3] + 1;
cin >> y[1] >> y[2] >> A[4] >> B[4] >> C[4] >> M[4];
for(int i = 3; i <= K; i++)
y[i] = (A[4] * y[i - 1] + B[4] * y[i - 2] + C[4]) % M[4] + 1;
int len = getRelevantTowers();
int ans = 0;
for(int i = 1; i <= K; i++)
{
int L = lower_bound(relevantT + 1, relevantT + 1 + len, x[i]) - relevantT;
//和塔重叠
if(x[i] == relevantT[L])
{
if(ok(i, valueAndID[relevantT[L]]))
ans++;
continue;
}
//该气球在最左边
if(L == 1)
{
if(ok(i, valueAndID[relevantT[L]]))
ans++;
continue;
}
//该气球在最右边
if(L > len)
{
if(ok(i, valueAndID[relevantT[len]]))
ans++;
continue;
}
//该气球处在两个塔之间
int LL = L - 1;
int RR = L;
if(ok(i, valueAndID[relevantT[LL]]))
{
ans++;
continue;
}
if(ok(i, valueAndID[relevantT[RR]]))
ans++;
}
cout << "Case #" << cas <<": " << ans <<endl;
}
return 0;
}