HDU 3074 Multiply game【线段树||zkw线段树||扩展gcd*乘法逆元||欧拉定理】

来源:互联网 发布:杜特软件下载 编辑:程序博客网 时间:2024/06/05 21:56

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2818 Accepted Submission(s): 1002

Problem Description

Tired of playing computer games, alpc23 is planning to play a game on numbers. Because plus and subtraction is too easy for this gay, he wants to do some multiplication in a number sequence. After playing it a few times, he has found it is also too boring. So he plan to do a more challenge job: he wants to change several numbers in this sequence and also work out the multiplication of all the number in a subsequence of the whole sequence.
To be a friend of this gay, you have been invented by him to play this interesting game with him. Of course, you need to work out the answers faster than him to get a free lunch, He he…

Input

The first line is the number of case T (T<=10).
For each test case, the first line is the length of sequence n (n<=50000), the second line has n numbers, they are the initial n numbers of the sequence a1,a2, …,an,
Then the third line is the number of operation q (q<=50000), from the fourth line to the q+3 line are the description of the q operations. They are the one of the two forms:
0 k1 k2; you need to work out the multiplication of the subsequence from k1 to k2, inclusive. (1<=k1<=k2<=n)
1 k p; the kth number of the sequence has been change to p. (1<=k<=n)
You can assume that all the numbers before and after the replacement are no larger than 1 million.

Output

For each of the first operation, you need to output the answer of multiplication in each line, because the answer can be very large, so can only output the answer after mod 1000000007.

Sample Input

1
6
1 2 4 5 6 3
3
0 2 5
1 3 7
0 2 5

Sample Output

240
420

Source

2009 Multi-University Training Contest 17 - Host by NUDT

Recommend

lcy | We have carefully selected several similar problems for you: 3069 3070 3071 3072 3073

题意:给出一个区间,查询区间乘积,单点更新。

对于线段树的掌握不够熟练,慢慢补出所有题解!

普通线段树(AC):

#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>using namespace std;#define rt x<<1|1#define lt x<<1const int maxn = 8 * 5 * 1e4;#define ll long long intconst ll p = 1e9 + 7;ll sum[maxn], a[maxn / 8];void init(){}void build(int x, int L, int R){    if (L == R)    {        sum[x] = a[L];        sum[x] %= p;//(sum[x]=a[L])%=p     }    else    {        int mid = (L + R) / 2;        build(lt, L, mid);        build(rt, mid + 1, R);        sum[x] = sum[lt] * sum[rt];//(sum[x]=sum[lt]*sum[rt])%=p;        sum[x] %= p;    }}void change(int x, int L, int R, int i, ll k){    if (L == R&&R == i)    {        sum[x] = k;        sum[x] %= p;    }    else    {        int mid = (L + R) / 2;        if (i <= mid)            change(lt, L, mid, i, k);        if (mid < i)            change(rt, mid + 1, R, i, k);        sum[x] = sum[lt] * sum[rt];        sum[x] %= p;    }}ll query(int x, int L, int R, int LL, int RR){    ll ans = 1;    if (LL <= L&&RR >= R)    {        ans = sum[x];        ans %= p;    }    else    {        int mid = (L + R) / 2;        if (LL <= mid)        {            ans *= query(lt, L, mid, LL, RR);            ans %= p;        }        if (mid < RR)        {            ans *= query(rt, mid + 1, R, LL, RR);            ans %= p;        }    }    return ans;}int main(){    int t;    scanf("%d", &t);    while (t--)    {        int n;        scanf("%d", &n);        for (int i = 1; i <= n; i++)        {            scanf("%lld", &a[i]);        }        build(1, 1, n);        int q;        scanf("%d", &q);        while (q--)        {            int t1, t2, t3;            scanf("%d %d %d", &t1, &t2, &t3);            if (t1)            {                change(1, 1, n, t2, 1ll * t3);            }            else            {                ll ans = 0;                ans = query(1, 1, n, t2, t3);                printf("%lld\n", ans);            }        }    }    return 0;}

扩展欧几里得(TLE):
顺带着 扩展gcd求乘法逆元的模板。
数据加强过了,再研究研究有没有可以突破的地方。

#include<iostream>#include<cstring>#include<cstdio>#include<cmath>#include<algorithm>using namespace std;#define rt x<<1|1#define lt x<<1#define ll long long intconst int maxn = 5 * 1e4 + 10;const ll low(ll x) { return x&-x; }const ll p = 1e9 + 7;ll f[maxn], a[maxn];ll t, x, y, n, m, X, Y, Z;void init() {    for (ll i = 0; i <= n; i++)        f[i] = 1;}/* a*x + b*y = gcd *//*扩展欧几里得推导 呱唧呱唧呱唧*//*    a%b = a - (a/b)*b    */  //取余函数/*     gcd = b*x1 + (a-(a/b)*b)*y1            = b*x1 + a*y1 – (a/b)*b*y1             = a*y1 + b*(x1 – a/b*y1)        */             /*         x = y1                        y = x1 – a / b*y1                 */int e_gcd(int a, int b, int &x, int &y){    if (b == 0)    {        x = 1;        y = 0;        return a;    }    int ans = e_gcd(b, a%b, x, y);    int temp = x;    x = y;    y = temp - a / b*y;    /*          x = y1                y = x1 – a / b*y1                 */    return ans;}int cal(int a, int m)//求逆元{    int x, y;    int gcd = e_gcd(a, m, x, y);    if (1 % gcd != 0)        return -1;    x *= 1 / gcd;    m = abs(m);    int ans = (x%m + m) % m;    return ans;}ll mod(const ll a){    return (a%p + p) % p;}ll inv(ll x, ll m){    if (x == 1)        return x;    return inv(m%x, m)*(m - m / x) % m;}ll sum(ll x){    ll tot = 1;    for (ll i = x; i; i -= low(i))        tot = mod(tot*f[i]);    return tot;}void change(ll x, ll y){    for (ll i = x; i <= n; i += low(i))        f[i] = mod(f[i] * y);}int main(){    //int t;    scanf("%lld", &t);    while (t--)    {        //int n;        scanf("%lld", &n);        init();        for (ll i = 1; i <= n; i++)        {            scanf("%lld", &a[i]);            change(i, a[i]);        }        scanf("%lld", &m);        while (m--)        {            scanf("%lld %lld %lld", &X, &Y, &Z);            if (X)            {                x = inv(a[Y], p);                change(Y, mod(Z*x));                a[Y] = Z;            }            else            {                x = inv(sum(Y - 1), p);                printf("%lld\n", mod(sum(Z)*x));            }        }    }    return 0;}
阅读全文
0 0
原创粉丝点击