SRM623D1Med CatchTheBeat

これは典型だけど苦手

問題概要(原文)

$N$ 個のものが格子点 $(x_i,y_i)$ にある. これらのものは自然落下し,$1$ 秒につき $1$ だけ $y$ 座標が小さくなる.

$(0,0)$ に人が立っていて,$1$ 秒につき $1$ だけ $x$ 座標を増減できる.

人とものが重なると $1$ 点もらえる.最大何点もらえるか?

考察

人が $1$ 秒につき $1$ だけ上に登っていくことにする.

すると,人は $(-1,1),(0,1),(1,1)$ のどれかの方向にだけ移動できることになる. ここで, $(x,y)$ を $(y-x,y+x)$ に回転させる.すると,人は $(2,0),(1,1),(0,2)$ 方向に移動できる. つまり,人は $(0,0)$ から自分の右上にあるものしか取れないことになる.

あとはこれらの点を $(x,y)$ 座標でソートしたあとに,典型的なLISを求める要領で最長非減少部分列を求めればよい.計算量は $O(NlogN)$.

ソースコード

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Debug = System.Diagnostics.Debug;
using StringBuilder = System.Text.StringBuilder;
public class CatchTheBeat
{
    public int maxCatched(int n, int x0, int y0, int a, int b, int c, int d, int mod1, int mod2, int offset)
    {
        var X = new int[n];
        var Y = new int[n];
        X[0] = x0; Y[0] = y0;
        for (int i = 1; i < n; i++)
        {
            X[i] = (int)((Math.BigMul(X[i - 1], a) + b) % mod1);
            Y[i] = (int)((Math.BigMul(Y[i - 1], c) + d) % mod2);
        }
        for (int i = 0; i < n; i++)
            X[i] -= offset;
        return f(X, Y);
    }
    int f(int[] X, int[] Y)
    {
        var n = X.Length;
        var Z = new KeyValuePair<int, int>[n];
        for (int i = 0; i < n; i++)
            Z[i] = new KeyValuePair<int, int>(Y[i] - X[i], X[i] + Y[i]);
        Array.Sort(Z, (l, r) =>
        {
            var cmp = l.Key.CompareTo(r.Key);
            if (cmp != 0) return cmp;
            return l.Value.CompareTo(r.Value);
        });
        var dp = new long[n + 50];
        for (int i = 0; i < n + 50; i++)
            dp[i] = 1000000000000;
        foreach (var x in Z)
        {
            if (x.Key < 0 || x.Value < 0) continue;
            var p = dp.UpperBound(x.Value);
            dp[p] = x.Value;
        }
        for (int i = n; i >= 0; i--)
        {
            if (dp[i] < dp[n + 48]) return i + 1;
        }
        return 0;
    }
}
static public partial class Algorithm
{

    static private int binarySearch<T>(this T[] a, int idx, int len, T v, Comparison<T> cmp, bool islb)
    {
        int l = idx;
        int h = idx + len - 1;
        while (l <= h)
        {
            int i = l + ((h - l) >> 1);
            int ord;
            if (a[i] == null || v == null) return -1;
            else ord = cmp(a[i], v);
            if (ord < 0) l = i + 1;
            else if (ord == 0)
            {
                if (!islb) l = i + 1;
                else h = i - 1;
            }
            else h = i - 1;
        }

        return l;
    }
    static public int UpperBound<T>(this T[] a, int idx, int len, T v, Comparison<T> cmp) { return binarySearch(a, idx, len, v, cmp, false); }
    static public int UpperBound<T>(this T[] a, T v) where T : IComparable<T> { return UpperBound(a, 0, a.Length, v, Comparer<T>.Default.Compare); }
}

コメント

  • データ構造ゲーだと思ったら違った