HDU5930-GCD

GCD

                                                                      Time Limit: 9000/4500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
                                                                                                Total Submission(s): 343    Accepted Submission(s): 78


Problem Description
Mr. Frog likes generating numbers! He can generate many numbers from a sequence.

For a given sequence  a1,a2,,an  Mr. Frog can choose two numbers l and r ( 1lrn ) and calculate the gcd between l-th and r-th number in this sequence  g=gcd(al,al+1,,ar) . Asan expert in generating numbers, Mr. Frog wants to know how many distinct numbers can be generated by a sequence.

Mr. Frog likes challenges, so there may be many modifications in this sequence. In the i-th modification, Mr. Frog may change  ap  to  vi . After each modification, you are asked to tell how many distinct numbers can be generated by this sequence immediately!
 

Input
The first line contains only one integer T, which indicates the number of test cases.

For each test case, the first line includes two numbers n, q( 1n,q50000 ). which indicate the length of sequence and the number of modifications.

The second line contains n numbers: a1,a2,,an .

Then q lines, each line contain two numbers,  pi,vi(1pin,1vi1000000) .

Test data guarantee that  1<ai1000000  all the time and the sum of all n and q is less than or equal to  2×105 .
 

Output
For each test case, first output one line "Case #x:", where x is the case number (starting from 1). Then q lines, each line contain only one number, which is the answer to current sequence.
 

Sample Input
  
  
2 3 2 1 2 3 1 3 2 3 3 2 3 3 3 1 1 2 2
 

Sample Output
  
  
Case #1: 3 1 Case #2: 2 3
Hint
For case 1, after the first operation, 3,2,1 can be generated by the sequence 3, 2, 3. Whereas after the second operation, sequence 3, 3, 3 can generate only 3.
 

Source
 

Recommend
wange2014
 

题意:给一个序列,每次改变一个位置的值,求每次更新后有多少种不同的连续的序列的gcd

解题思路:先一遍扫出一共有几种gcd,每种gcd有几个,建一棵线段树用来求区间gcd。每次修改一个位置的值时,先分别向左和向右求出未更新前以这个位置为端点有几种gcd,每种gcd有几个,然后向左和向右产生的gcd合并算一下,每种gcd减去产生的数量。然后修改线段树上的gcd,再向左和向右求出更新后以这个位置为端点有几种gcd,每种gcd有几个,然后向左和向右产生的gcd合并算一下,每种gcd加上产生的数量



#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <string>  
#include <algorithm>  
#include <queue>  
#include <stack>  
#include <cmath>  
#include <map>  
#include <bitset>  
#include <set>  
#include <vector>  
#include <functional>  
  
using namespace std;  
  
#define LL long long  
const int INF = 0x3f3f3f3f;  
  
int n, q;  
int a[50009], xx[50009 * 4], sum;  
LL visit[1000009];  
  
struct node  
{  
    int x, sum;  
} b[50009], c[50009];  
  
int gcd(int x, int y)  
{  
    return x%y ? gcd(y, x%y) : y;  
}  
  
void build(int k, int l, int r)  
{  
    if (l == r) { xx[k] = a[l]; return; }  
    int mid = (l + r) >> 1;  
    build(k << 1, l, mid);  
    build(k << 1 | 1, mid + 1, r);  
    xx[k] = gcd(xx[k << 1], xx[k << 1 | 1]);  
}  
  
void update(int k, int l, int r, int p, int val)  
{  
    if (l == r) { xx[k] = val; a[p] = val; return; }  
    int mid = (l + r) >> 1;  
    if (p <= mid) update(k << 1, l, mid, p, val);  
    else update(k << 1 | 1, mid + 1, r, p, val);  
    xx[k] = gcd(xx[k << 1], xx[k << 1 | 1]);  
}  
  
int query(int k, int l, int r, int ll, int rr)  
{  
    if (rr < ll) return 0;  
    if (ll <= l&&r <= rr) return xx[k];  
    int mid = (l + r) >> 1;  
    int ans1 = 0, ans2 = 0;  
    if (ll <= mid) ans1 = query(k << 1, l, mid, ll, rr);  
    if (rr > mid) ans2 = query(k << 1 | 1, mid + 1, r, ll, rr);  
    if (ans1&&ans2) return gcd(ans1, ans2);  
    if (ans1) return ans1;  
    else return ans2;  
}  
  
int queryl(int k, int l, int r, int rr, int val)  
{  
    if (xx[k] % val == 0) return l;  
    if (l == r) return 0;  
    int mid = (l + r) >> 1, res1, res2;  
    if (rr > mid)  
    {  
        res2 = queryl(k << 1 | 1, mid + 1, r, rr, val);  
        if (res2 == mid + 1)  
        {  
            res1 = queryl(k << 1, l, mid, rr, val);  
            if (!res1) return res2;  
            else return res1;  
        }  
        return res2;  
    }  
    return queryl(k << 1, l, mid, rr, val);  
}  
  
int queryr(int k, int l, int r, int ll, int val)  
{  
    if (xx[k] % val == 0) return r;  
    if (l == r) return 0;  
    int mid = (l + r) >> 1,res1,res2;  
    if (ll <= mid)  
    {  
        res1 = queryr(k << 1, l, mid, ll, val);  
        if (res1 == mid)  
        {  
            res2 = queryr(k << 1 | 1, mid + 1, r, ll, val);  
            if (!res2) return res1;  
            return res2;  
        }  
        return res1;  
    }  
    return queryr(k << 1 | 1, mid + 1, r, ll, val);  
}  
  
void f(int p, int flag)  
{  
    int cnt1 = 1, cnt2 = 1, k = p, kk = a[p];  
    b[cnt1].x = a[p];  
    while (k >= 1)  
    {  
        int ans = queryl(1, 1, n, p, kk);  
        b[cnt1++].sum = (k - ans + 1);  
        k = ans - 1;  
        kk = query(1, 1, n, k, p);  
        b[cnt1].x = kk;  
    }  
    k = p, kk = a[p];  
    c[cnt2].x = a[p];  
    while (k <= n)  
    {  
        int ans = queryr(1, 1, n, p, kk);  
        c[cnt2++].sum = (ans - k + 1);  
        k = ans + 1;  
        kk = query(1, 1, n, p, k);  
        c[cnt2].x = kk;  
    }  
    for (int i = 1; i < cnt1; i++)  
    {  
        for (int j = 1; j < cnt2; j++)  
        {  
            k = gcd(b[i].x, c[j].x);  
            if (!visit[k]) sum++;  
            if (flag) visit[k] += 1LL * b[i].sum*c[j].sum;  
            else visit[k] -= 1LL * b[i].sum*c[j].sum;  
            if (!visit[k]) sum--;  
        }  
    }  
}  
  
int main()  
{  
    int t, cas = 0;  
    scanf("%d", &t);  
    while (t--)  
    {  
        printf("Case #%d:\n", ++cas);  
        scanf("%d%d", &n, &q);  
        memset(visit, 0, sizeof visit);  
        sum = 0;  
        stack<pair<int, int>>s1, s2;  
        for (int i = 1; i <= n; i++)  
        {  
            scanf("%d", &a[i]);  
            s1.push(make_pair(a[i], 1));  
            while (!s1.empty()) s2.push(make_pair(gcd(s1.top().first, a[i]), s1.top().second)), s1.pop();  
            while (!s2.empty())  
            {  
                pair<int, int>pre = s2.top(); s2.pop();  
                if (!visit[pre.first]) sum++;  
                visit[pre.first] += pre.second;  
                while (!s1.empty() && s1.top().first == pre.first) pre.second += s1.top().second, s1.pop();  
                s1.push(pre);  
            }  
        }  
        build(1, 1, n);  
        while (q--)  
        {  
            int p, v;  
            scanf("%d%d", &p, &v);  
            f(p, 0);  
            update(1, 1, n, p, v);  
            f(p, v);  
            printf("%d\n", sum);  
        }  
    }  
    return 0;  
}  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值