Target Practice (USACO Silver 2023 December)

Target Practice (USACO Silver 2024 January)

总体是一个有点思考难度的模拟题。

因为最多只修改一个动作,所以从这个动作看出去,前面部分结果不变,当前动作可能改结果,而后面所有动作的结果可能改变。

主要就是要快速计算后面的动作的改变,每个的变化可能是五种之一,就是左移2,左移不变,右移1和右移2。

具体实现上,需要一个“离线计算”的思想,就是要把从后往前5种情况下,所有目标位置击中次数记录下来,然后倒着模拟(逐步减少击中次数),这样当击中次数从1减至0的时候,总的分值就减少1,这样就可以把后面的部分的5种情况下的分值模拟出来。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int t, c;
char s[100005];
set<int> targets;
map<int,int> left;
map<int,int> right[5];     // position -> # of hits, 0: left 2, 1: left 1, 2: no change, 3: right 1, 4: right 2
int ans;
 
int main() {
    scanf("%d %d", &t, &c);
    for (int i = 0; i < t; i++) {
        int p;
        scanf("%d", &p);
        targets.insert(p);
    }
    scanf("%s", s);
    int p = 0;
    // prepare all right lists
    for (int i = 0; i < c; i++) {
        if (s[i] == 'L') p--;
        if (s[i] == 'R') p++;
        if (s[i] == 'F') {
            if (targets.count(p-2)) right[0][p-2] += targets.count(p-2);
            if (targets.count(p-1)) right[1][p-1] += targets.count(p-1);
            if (targets.count(p))   right[2][p]   += targets.count(p);
            if (targets.count(p+1)) right[3][p+1] += targets.count(p+1);
            if (targets.count(p+2)) right[4][p+2] += targets.count(p+2);
        }
    }
    // simulate all moves
    p = 0;
    for (int i = 0; i < c; i++) {
        int res = left.size();
        int newhit = left.count(p) ? 0 : targets.count(p);  // if this is a F, does it count?
        if (s[i] == 'L') {
            if (right[3].count(p))                          // change to F, rest move right
                ans = max(ans, res + (int)right[3].size());
            else
                ans = max(ans, res + newhit + (int)right[3].size());
            ans = max(ans, res + (int)right[4].size());     // change to R, rest move right 2 steps
            p--;
        } else if (s[i] == 'R') {
            if (right[1].count(p))                          // change to F, rest move left
                ans = max(ans, res + (int)right[1].size());
            else
                ans = max(ans, res + newhit + (int)right[1].size());
            ans = max(ans, res + (int)right[0].size());     // change to L, rest move left 2 steps
            p++;
        } else {        // F
            for (int j = 0; j < 5; j++)                     // count down all right lists
                if (right[j].count(p+j-2) && --right[j][p+j-2] == 0)      
                    right[j].erase(p+j-2);
            ans = max(ans, res + (int)right[1].size());     // change to L, move left 1
            ans = max(ans, res + (int)right[3].size());     // change to R, move right 1
 
            if (targets.count(p) && left[p] == 0) {       // add hit to left list and remove from rest lists
                left[p]++;
                for (int j = 0; j < 5; j++) right[j].erase(p);
            }
        }
    }
    ans = max(ans, (int)left.size());    // no change
 
    printf("%d\n", ans);
 
    return 0;
}