はまやんはまやんはまやん

hamayanhamayan's blog

DigitRotation [SRM 736 Div1 Easy]

http://community.topcoder.com/stat?c=problem_statement&pm=14958

とても大きい数Xがある。
この数に対して、以下の操作を行う。
 

  • a<b<cを用意し、a桁目をb桁目に、b桁目をc桁目に、c桁目をa桁目に数を移動させる
  • このとき、leading-zeroとなるのは不正な操作である

 
異なるa,b,cについて以上の操作を行うとき、有効な操作によって得られる数の総和をmod 998244353で求めよ。

解法

|X|=nとする。
本当はO(n^3)で解けるのだが、以下はO(n^2)で解いてある。
 
a,cを固定して、bをまとめて計算することにする。
不正な操作はa=0であり、X[c]が'0'の場合はleading-zeroとなってしまうので、この場合はスキップしよう。
それ以外の場合を考える。
 
m=c-a-1としておく。これはbの組み合わせ数である。
sm[i] := i桁目の数そのもの
P[i] := i桁目の基数の累乗(最下位桁から1, 10, 100, ...)
A[i] := sm[i] * P[i]
これを全て累積和を取れるようにしておく。
getsm, getPP, getA(l, r) := [l,r]での累積和
各計算の簡単な説明を以下に残しておく。
 
ans += getA(0, a - 1) * m;
m通りの数に対して、[0,a)の部分は同じ数になる。
 
ans += mint(X[c] - '0') * P[a] * m;
操作によりc桁目の数がa桁目に移動する。
c桁目の数はa桁目にm回現れることになる。
 
ans += getsm(a + 1, c - 1) * P[c];
操作によりb桁目の数がc桁目に移動する。
b桁目の数としてありえるのが、[a+1,c)の数全てであるため、その総和を取ってc桁目として足しておく。
 
ans += mint(X[a] - '0') * getPP(a + 1, c - 1);
a桁目の数はb桁目に移動する。
b桁目はa+1桁目、a+2桁目、..., c-1桁目になりうるので、この全パターンを足す。
[a+1,c)桁目の基数の累乗の総和より、計算しておこう。
 
ans += getA(a + 1, c - 1) * (m - 1);
[a,c)の間のb桁目以外の数はそのまま残る。各桁m-1回同じ位置に現れることになる。
 
ans += getA(c + 1, n - 1) * m;
m通りの数に対して、[c+1,n)の部分は同じ数になる。

mint P[505], PP[505];
mint getPP(int l, int r) {
    if (l > r) return 0;
    mint res = PP[r];
    if (l) res -= PP[l - 1];
    return res;
}
mint sm[505];
mint getsm(int l, int r) {
    if (l > r) return 0;
    mint res = sm[r];
    if (l) res -= sm[l - 1];
    return res;
}
mint A[505];
mint getA(int l, int r) {
    if (l > r) return 0;
    mint res = A[r];
    if (l) res -= A[l - 1];
    return res;
}
struct DigitRotation {
    int sumRotations(string X) {
        int n = X.length();

        P[0] = 1;
        rep(i, 1, n) P[i] = P[i - 1] * 10;
        reverse(P, P + n);

        PP[0] = P[0];
        rep(i, 1, n) PP[i] = PP[i - 1] + P[i];

        sm[0] = X[0] - '0';
        rep(i, 1, n) sm[i] = sm[i - 1] + mint(X[i] - '0');

        A[0] = mint(X[0] - '0') * P[0];
        rep(i, 1, n) A[i] = A[i - 1] + mint(X[i] - '0') * P[i];

        mint ans = 0;
        rep(a, 0, n) rep(c, a + 2, n) {
            if (a == 0 and X[c] == '0') continue;
            int m = c - a - 1;

            ans += getA(0, a - 1) * m;
            ans += mint(X[c] - '0') * P[a] * m;
            ans += getsm(a + 1, c - 1) * P[c];
            ans += mint(X[a] - '0') * getPP(a + 1, c - 1);
            ans += getA(a + 1, c - 1) * (m - 1);
            ans += getA(c + 1, n - 1) * m;
        }

        return ans.get();
    }
};