mayoko’s diary

プロコンとかいろいろ。

CodeChef Chef and cities

問題

Contest Page | CodeChef

数列 F が与えられる。以下のクエリを処理せよ。

  • F[p] = f とする
  • R が与えられるので, F[0]*F[R]*F[2*R]*...*F[(N-1)/R*R] の値について, 10 進法における一番上の数, 10^9+7 で割ったあまり を求める
解法

自分の解法だと 100 点取ろうとしたときに 5 点分が確保できなかったので 5 点分の時は別に処理しています。

10 進法の一番上の数を求めるときは, log を取るのがよくやることです(大学受験でよく出るアレ)。

クエリの処理の仕方ですが, 取り合えず最初に mult[i] = (R = i としたときの 10^9+7 で割ったあまり), logs[i] = (R = i としたときの log[F[i*d]] みたいなやつの和) を求めておきます。これは N*(1+1/2+...+1/N) なので O(N log N) でできます。

また, F[p] = f と変更したとき, mult, logs が変化するのは, p の約数の部分だけです。10^5 以下で約数が最大のものは 128 個なので, 十分高速に mult, logs の必要な部分を更新できます。

ll mod_pow(ll x, ll p, ll MOD) {
    ll a = 1;
    while (p) {
        if (p%2) a = a*x%MOD;
        x = x*x%MOD;
        p/=2;
    }
    return a;
}

// mod_inverse
ll mod_inverse(ll a, ll m) {
    return mod_pow(a, m-2, m);
}

const int MOD = 1e9+7;
const int MAXN = 100100;
int F[MAXN];
Real logs[MAXN]; 
ll mult[MAXN];
vector<int> divs[MAXN];

Real cand[15];

int main() {
    for (int i = 1; i <= 10; i++) 
        cand[i] = log10l(i);
    for (int i = 1; i < MAXN; i++) {
        for (int j = 1; i*j < MAXN; j++) {
            divs[i*j].push_back(i);
        }
    }
    int N;
    scanf("%d", &N);
    for (int i = 0; i < N; i++) 
        scanf("%d", F+i);
    if (N <= 10) {
        int Q;
        cin >> Q;
        while (Q--) {
            int type;
            scanf("%d", &type);
            if (type == 1) {
                int p, f;
                cin >> p >> f;
                F[p-1] = f;
            } else {
                int R;
                cin >> R;
                ll ans = 1;
                for (int i = 0; i < N; i += R) {
                    ans *= F[i];
                }
                printf("%c %lld\n", to_string(ans)[0], ans%MOD);
            }
        }
    } else {
        for (int i = 1; i <= N; i++) {
            mult[i] = 1;
            for (int j = i; j < N; j+=i) {
                (mult[i] *= F[j]) %= MOD;
                logs[i] += log10l(F[j]);
            }
        }
        int Q;
        scanf("%d", &Q);
        while (Q--) {
            int type;
            scanf("%d", &type);
            if (type == 1) {
                int p, f;
                scanf("%d %d", &p, &f);
                p--;
                if (p) {
                    auto div = divs[p];
                    Real to = log10l(f), from = log10l(F[p]);
                    ll inv = mod_inverse(F[p], MOD);
                    for (int d : div) {
                        (mult[d] *= f) %= MOD;
                        (mult[d] *= inv) %= MOD;
                        logs[d] += to - from;
                    }
                }
                F[p] = f;
            } else {
                int R;
                scanf("%d", &R);
                Real tmp = logs[R] + log10l(F[0]);
                Real rest = tmp - (int)(tmp);
                int ans1 = 0;
                for (int i = 1; i < 10; i++) {
                    if (cand[i] <= rest && rest < cand[i+1]) {
                        ans1 = i;
                        break;
                    }
                }
                ll ans2 = (mult[R]*F[0]) % MOD;
                printf("%d %lld\n", ans1, ans2);
            }
        }
    }
    return 0;
}