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;
}