mayoko’s diary

プロコンとかいろいろ。

SRM 573 div1 hard: WolfPack

解法

45 度回転させることを考えます。もとの x, y に対して, x' = x+y, y' = x-y とすると, 各移動に対して, x', y' の遷移は以下のようになります。

  • x++ -> x'++, y'++
  • x-- -> x'--, y'--
  • y++ -> x'++, y'--
  • y-- -> x'--, y'++

これで何に注目すべきかというと, x' の増減と y' の増減は独立したものと考えられるということです。つまり,
「x 軸に対して m 回 ±1 だけ移動してある一つの場所に集まる」という問題と, 「y 軸に対して m 回 ±1 だけ移動してある一つの場所に集まる」という問題に分かれます。

これが分かれば後は div2 hard と同じですね。集まる地点 tx に対して, 今いる地点 x が d だけずれていたとすると, C[m][(m+d)/2] というのが, x にいる頂点の tx に向かう場合の数です。

45 度回転すると x, y について±1 を独立に考えられるというのが面白い問題でした。

const int MAXN = 200000;
const ll MOD = 1e9+7;
ll fact[MAXN], factinv[MAXN];
// extgcd
ll extgcd(ll a, ll b, ll& x, ll& y) {
    ll d = a;
    if (b != 0) {
        d = extgcd(b, a % b, y, x);
        y -= (a / b) * x;
    } else {
        x = 1; y = 0;
    }
    return d;
}
// mod_inverse
ll mod_inverse(ll a, ll m) {
    ll x, y;
    extgcd(a, m, x, y);
    return (m+x%m) % m;
}
ll nCr(int n, int r) {
    return ((fact[n]*factinv[r])%MOD)*factinv[n-r] % MOD;
}

ll f(const vector<int>& x, int m) {
    int n = x.size();
    ll ret = 0;
    for (int tx = -300000; tx <= 300000; tx++) {
        ll mult = 1;
        for (int i = 0; i < n; i++) {
            int d = abs(tx-x[i]);
            if (d > m || (m+d)%2 == 1) {
                mult = 0;
                break;
            }
            (mult *= nCr(m, (m-d)/2)) %= MOD;
        }
        (ret += mult) %= MOD;
    }
    return ret;
}

class WolfPack {
public:
    int calc(vector <int> xx, vector <int> yy, int m) {
        fact[0] = factinv[0] = 1;
        for (int i = 1; i < MAXN; i++) {
            fact[i] = (fact[i-1]*i)%MOD;
            factinv[i] = mod_inverse(fact[i], MOD);
        }
        int n = xx.size();
        vector<int> x(n), y(n);
        for (int i = 0; i < n; i++) {
            x[i] = xx[i]+yy[i];
            y[i] = xx[i]-yy[i];
        }
        ll ans = f(x, m) * f(y, m) % MOD;
        return (int)(ans);
    }
};