HDU 5930 GCD

来源:互联网 发布:javac 多个java文件 编辑:程序博客网 时间:2024/06/18 07:41

HDU 5930 GCD

线段树上二分

题意

设计数据结构支持:

  • 单点修改
  • 查询整个区间以及每个子区间有多少种不同的区间gcd

思路

线段树上的二分。

%%%

大体思路是这样:初始区间给定后,可以计算出每个gcd有多少个区间,用g数组记录一下。(因为最大的数字不超过1000000)
线段树维护区间gcd。每次修改,假设修改pos处的值,先计算所有受pos影响的区间的gcd与个数,在g数组上减去这些;在线段树单点修改pos的值,重复上述操作,在g数组上加回来。

问题就是怎么计算受pos影响的区间的gcd。两个query函数的变种:queryl(pos,gcdval,l,r,rt)queryr(pos,gcdval,l,r,rt)。负责查询pos处左侧(右侧)第一个与gcdval不同的gcd的值以及位置,亦即以pos为右(左)端点的直到数列尽头的,每个gcd不同的区间。这样每次查的结果存到数组,pos左侧存一个数组,pos右侧存一个数组,最后两个数组组合一下,更新g。

queryl为例,说一下:

//查询时//R落在[l,mid],这时直接递归到左儿子//R落在[mid+1,r],这时先查一下右儿子,如果gcd小于p,说明gcd变化了,那么返回查询结果//如果gcd>=p,说明目前查到的[mid+1,pos]这些数都是pos这数的倍数,gcd没有变化,要再查左儿子//递归基有两种情况,某个完全在左侧的区间的公约数%p==0,说明这些数全比pos处大,直接返回p就行,或者查到了叶子,也要返回//感觉好复杂,自己理解吧pair<int, int> queryl(int R, int p, int l, int r, int rt){    if(r<=R)    {        if(stree[rt]%p==0) return make_pair(p, l);        else if(l==r) return make_pair(gcd(p, stree[rt]), l);//l实际改成1也能过,是一样的    }    int mid=(l+r)>>1;    if(R<=mid) return queryl(R, p, lson);    pair<int, int> tmp=queryl(R, p, rson);    if(tmp.first<p) return tmp;    return queryl(R, p, lson);}

对了,初始统计g数组时也用了上面的方法。

代码

不知道以后遇到这种题能不能写得出来

#include <bits/stdc++.h>#define lson l,mid,rt<<1#define rson mid+1,r,rt<<1|1#define M(a,b) memset(a,b,sizeof(a))using namespace std;const int MAXN=50005, MAXG=1000007;int stree[MAXN<<2], num[MAXN];int g[MAXG];inline int gcd(int a, int b) { return b==0 ? a : gcd(b, a%b); }inline void pushup(int rt) { stree[rt]=gcd(stree[rt<<1], stree[rt<<1|1]); }void build(int l, int r, int rt){    if(l==r) { stree[rt]=num[l];return; }    int mid=(l+r)>>1;    build(lson);    build(rson);    pushup(rt);}void update(int pos, int val, int l, int r, int rt){    if(l==r)    {        stree[rt]=val;        return;    }    int mid=(l+r)>>1;    if(pos<=mid) update(pos, val, lson);    else update(pos, val, rson);    pushup(rt);}int query(int L, int R, int l, int r, int rt){    if(L<=l&&r<=R) return stree[rt];    int mid=(l+r)>>1;    if(mid>=R) return query(L, R, lson);    else if(L>mid) return query(L, R, rson);    else return gcd(query(L, R, lson), query(L, R, rson));}pair<int, int> queryl(int R, int p, int l, int r, int rt){    if(r<=R)    {        if(stree[rt]%p==0) return make_pair(p, l);        else if(l==r) return make_pair(gcd(p, stree[rt]), l);    }    int mid=(l+r)>>1;    if(R<=mid) return queryl(R, p, lson);    pair<int, int> tmp=queryl(R, p, rson);    if(tmp.first<p) return tmp;    return queryl(R, p, lson);}pair<int, int> queryr(int L, int p, int l, int r, int rt){    if(L<=l)    {        if(stree[rt]%p==0) return make_pair(p, l);        else if(l==r) return make_pair(gcd(p, stree[rt]), l);    }    int mid=(l+r)>>1;    if(L>mid) return queryr(L, p, rson);    pair<int, int> tmp=queryr(L, p, lson);    if(tmp.first<p) return tmp;    return queryr(L, p, rson);}void calc(int p, int f, int &s, int n){    vector<pair<int, int>> pl, pr;    int nowgcd, finalgcd, curpos;    nowgcd=num[p], finalgcd=query(1, p, 1, n, 1), curpos=p;    pl.push_back(make_pair(nowgcd, p));    while(nowgcd!=finalgcd)    {        pair<int, int> tmp=queryl(curpos, nowgcd, 1, n, 1);        pl.push_back(tmp);        nowgcd=tmp.first, curpos=tmp.second;    }    pl.push_back(make_pair(nowgcd, 0));    nowgcd=num[p], finalgcd=query(p, n, 1, n, 1), curpos=p;    pr.push_back(make_pair(nowgcd, p));    while(nowgcd!=finalgcd)    {        pair<int, int> tmp=queryr(curpos, nowgcd, 1, n, 1);        pr.push_back(tmp);        nowgcd=tmp.first, curpos=tmp.second;    }    pr.push_back(make_pair(nowgcd, n+1));    for(int i=1;i<pl.size();i++)    {        int lenl=pl[i-1].second-pl[i].second;        int gcdl=pl[i-1].first;        for(int j=1;j<pr.size();j++)        {            int lenr=pr[j].second-pr[j-1].second;            int gcdr=pr[j-1].first;            int num=f*lenl*lenr;            int tmpgcd=gcd(gcdl, gcdr);            if(g[tmpgcd]==0) s++;            g[tmpgcd]+=num;            if(g[tmpgcd]==0) s--;        }    }}int main(){    int T;    scanf("%d", &T);int cas=0;    while(T--)    {        printf("Case #%d:\n", ++cas);        int gcdsum=0;        M(g, 0);        int n, op;scanf("%d%d", &n, &op);        for(int i=1;i<=n;i++) scanf("%d", &num[i]);        build(1, n, 1);        for(int i=1;i<=n;i++)        {            int nowgcd=num[i], finalgcd=query(1, i, 1, n, 1), curpos=i;            while(nowgcd!=finalgcd)            {                pair<int, int> tmp=queryl(curpos, nowgcd, 1, n, 1);                if(!g[nowgcd]) gcdsum++;                g[nowgcd]+=curpos-tmp.second;                nowgcd=tmp.first, curpos=tmp.second;            }            if(!g[finalgcd]) gcdsum++;            g[finalgcd]+=curpos;        }        while(op--)        {            int p, v;scanf("%d%d", &p, &v);            calc(p, -1, gcdsum, n);            num[p]=v;            update(p, v, 1, n, 1);            calc(p, 1, gcdsum, n);            printf("%d\n", gcdsum);        }    }    return 0;}