1. 程式人生 > >Kickstart Round D 2018 B題 Paragliding

Kickstart Round D 2018 B題 Paragliding

題目大意:有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;
}