码迷,mamicode.com
首页 > 其他好文 > 详细

[ural 2124]. Algebra on Segment

时间:2019-11-04 09:33:23      阅读:72      评论:0      收藏:0      [点我收藏+]

标签:include   get   std   c++   make   code   git   oid   合并   

题意

给出一个模\(p\)\(p\)是素数)意义下的序列,支持两种操作:

  1. 区间乘一个数;
  2. 询问一个区间的元素构成的群的大小。

保证序列中的数时时刻刻不为\(0\)

题解

一道好题……sb了好久。
第一个想法显然是先找到一个原根\(g\),再取指标进行运算。
然后对于一个区间\(g ^ {k_l}, g ^ {k_{l + 1}}, \ldots, g ^ {k_r}\),生成群的生成元\(\omega\)\(g ^ {(\gcd{k_l, k_{l + 1}, \ldots, k_r, p - 1})}\),则群的大小为\(\frac{p - 1}{ind_g(\omega)}\)
看这个\(\gcd\),因为要区间修改,所以要维护原序列和原序列的差分两个数组,因为
\[ \gcd(x_1, x_2, \ldots, x_n) = \gcd(x_1, x_2 - x_1, \ldots, x_n - x_{n - 1}) \]
这样便把区间修改变成单点修改。
复杂度是\(\mathcal O((n + q) \sqrt {(n + q) p} + q \log ^ 2 n)\),因为要bsgs求指标(我tmbsgs块大小还写挂了)。
然而这样是过不了的。那咋整啊?
XZA说指标的信息有冗余,实际上只需要求阶即可(求阶是\(\mathcal O(\log ^ 2 p)\)的)。然而当时并没有理解这句话的意思,尽管感性理解好像是这回事。
考虑直接用阶做。
假设我们已经知道了区间\([l, r]\)中每个元素的阶,那么如何求其生成群的大小?考虑两两合并。
对于一个阶为\(a\)的元素和一个阶为\(b\)的元素,其生成的群可分别表示为\(<g ^ {\frac{p - 1}{a}}>, <g ^ {\frac{p - 1}{b}}>\)
那么合并之后,群可表示为\(<g ^ {\gcd{(\frac{p - 1}{a}}, \frac{p - 1}{b})}>\)。则新的群的大小为\(\frac{p - 1}{\gcd{(\frac{p - 1}{a}}, \frac{p - 1}{b})}\)
(这其中用到了一个很基础的定理:循环群的大小等于其生成元的阶。)
简单运算一下,发现合并后群的大小就等于\(\text{lcm} (a, b)\)。所以,这要求维护一个区间\(\text{lcm}\)。但是区间\(\text{lcm}\)也许并没有带区间乘的优秀做法。
实际上,我们可以维护一个商分,即\(a_i' = a_i * {a_{i - 1}} ^ {-1}\)。这样区间乘就变成了单点乘。
根据这题的询问是群,则\((a_l, a_{l + 1} * {a_l} ^ {-1}, \ldots, a_r * {a_{r - 1}} ^ {-1})\)形成的群和\((a_l, a_{l + 1}, \ldots, a_r)\)形成的群并没有区别,所以维护商分完全可行。
因此,大概只需要一棵线段树维护原序列,一棵线段树维护商分序列的每个元素的阶的区间\(\text{lcm}\)即可。
复杂度\(\mathcal O((n + q) \log ^ 2 n)\)。常数极大。(以上\(\log n\)\(\log p\)混用了,明白就好)

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef pair <int, int> pii;
const int N = 1e5 + 5;
int mod, _mod, n, q;
int a[N], g[N << 2], v[N << 2];
vector <pii> pf;
int lcm (int x, int y) {
    return x / __gcd(x, y) * y;
}
int power (int x, int y, int mod) {
    int ret = 1;
    for ( ; y; y >>= 1, x = 1ll * x * x % mod) {
        if (y & 1) {
            ret = 1ll * ret * x % mod;
        }
    }
    return ret;
}
void factorize (int x) {
    pf.clear();
    int t = sqrt(x + 1);
    for (int i = 2, c; i <= t; ++i) {
        if (x % i == 0) {
            c = 0;
            for ( ; x % i == 0; x /= i, ++c);
            pf.push_back(make_pair(i, c));
        }
    }
    if (x > 1) {
        pf.push_back(make_pair(x, 1));
    }
}
int ord (int x) {
    int ret = _mod;
    for (auto q : pf) {
        for (int i = 1; i <= q.se; ++i) {
            if (power(x, ret / q.fi, mod) == 1) {
                ret /= q.fi;
            } else {
                break;
            }
        }
    }
    return ret;
}
void build (int o, int l, int r) {
    v[o] = 1;
    if (l == r) {
        g[o] = v[o] = a[l];
        if (l) {
            g[o] = 1ll * g[o] * power(a[l - 1], mod - 2, mod) % mod;
        }
        g[o] = ord(g[o]);
        return;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid), build(o << 1 | 1, mid + 1, r);
    g[o] = lcm(g[o << 1], g[o << 1 | 1]);
}
void pushdown (int o) {
    if (v[o] != 1) {
        v[o << 1] = (1ll * v[o << 1] * v[o]) % mod;
        v[o << 1 | 1] = (1ll * v[o << 1 | 1] * v[o]) % mod;
        v[o] = 1;
    }
}
int query (int o, int l, int r, int x) {
    if (l == r) {
        return v[o];
    }
    pushdown(o);
    int mid = (l + r) >> 1;
    if (x <= mid) {
        return query(o << 1, l, mid, x);
    } else {
        return query(o << 1 | 1, mid + 1, r, x);
    }
}
void modify (int o, int l, int r, int x, int v) {
    if (l == r) {
        g[o] = 1ll * query(1, 1, n, l) * power(query(1, 1, n, l - 1), mod - 2, mod) % mod;
        g[o] = ord(g[o]);
        return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) {
        modify(o << 1, l, mid, x, v);
    } else {
        modify(o << 1 | 1, mid + 1, r, x, v);
    }
    g[o] = lcm(g[o << 1], g[o << 1 | 1]);
}
void modify (int o, int l, int r, int x, int y, int z) {
    if (x <= l && r <= y) {
        v[o] = 1ll * v[o] * z % mod;
        return;
    }
    pushdown(o);
    int mid = (l + r) >> 1;
    if (x <= mid) {
        modify(o << 1, l, mid, x, y, z);
    }
    if (y > mid) {
        modify(o << 1 | 1, mid + 1, r, x, y, z);
    }
}
int query (int o, int l, int r, int x, int y) {
    if (x <= l && r <= y) {
        return g[o];
    }
    int mid = (l + r) >> 1, ret = 1;
    if (x <= mid) {
        ret = lcm(ret, query(o << 1, l, mid, x, y));
    }
    if (y > mid) {
        ret = lcm(ret, query(o << 1 | 1, mid + 1, r, x, y));
    }
    return ret;
}
int read () {
    int x = 0; char ch = getchar();
    for ( ; !isdigit(ch); ch = getchar());
    for ( ; isdigit(ch); ch = getchar()) {
        x = x * 10 + ch - '0';
    }
    return x;
}
void write (int x) {
    if (x >= 10) {
        write(x / 10);
    }
    putchar('0' + x % 10);
}
int main () {
    mod = read(), n = read(), q = read();
    factorize(_mod = mod - 1);
    for (int i = 1; i <= n; ++i) {
        a[i] = read();
    }
    build(1, 1, n);
    for (int i = 1, o, l, r, x; i <= q; ++i) {
        o = read(), l = read(), r = read();
        if (o == 1) {
            x = read();
            modify(1, 1, n, l, r, x);
            modify(1, 1, n, l, x);
            if (r < n) {
                modify(1, 1, n, r + 1, -x);
            }
        } else {
            x = ord(query(1, 1, n, l));
            if (l < r) {
                x = lcm(x, query(1, 1, n, l + 1, r));
            }
            write(x), putchar('\n');
        }
    }
    return 0;
}

[ural 2124]. Algebra on Segment

标签:include   get   std   c++   make   code   git   oid   合并   

原文地址:https://www.cnblogs.com/psimonw/p/11790321.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!