from NOIP2016模拟题28

题目大意

n个点的序列,权值\(<=10^6\)

q个操作

1.单点修改

2.求所有区间gcd中,不同数个数

分析

1.以一个点为端点,向左或向右的gcd种数都只有\(\log Maxval\)种且收敛很快

1.权值较小可以用桶统计一个gcd的出现次数

做法1(正解)线段树上二分

  1. \(n \log n\)递推预处理出以每个点为右端点的gcd

    顺便记录每种gcd出现的最左位置,用于统计数量,更新到桶里

  2. 可以用一颗线段树维护单点修改,区间gcd

  3. 考虑一次修改x(可以看成一次删除+一次插入)

    影响的只是包含x的区间

    根据分析1,我们在线段树上二分

    搞出x向左的\(\log\)个gcd及出现的次数,和向右的....

    因为左边某个区间的所有数和右边某个区间的所有数两两gcd都相同

    \(\log^2\)更新答案

    复杂度\(\log^2\),再加个gcd 的log

做法2 线段树强行维护

每个点维护3个信息

到左的log个gcd(tl)

到右的log个gcd(tr)

整个区间的gcd(all)

pushup的时候更新tl,tr,O(\(\log\))

左儿子tr和右儿子tl弄在一起gcd一下,O(\(\log^2\)),gcd还有一个\(\log\)

对于删除,点x到根上统计的信息都无效了,直接删掉

在插入的时候重新统计

复杂度\(\log^3\),再加个gcd 的log

比赛时强行意识流,求tl,tr的时候不顺带求出出现次数直接当成1

然后现在想想好像并不会错→_→

solution

做法1

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=50007;
const int N=1000007;
typedef long long LL; inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(;isdigit(c);c=getchar()) x=x*10+c-48;
return f?x:-x;
} int n,m;
int a[M];
int ans; LL v[N];
void add(int d,LL num){
if(v[d]==0)ans++;
v[d]+=num;
}
void del(int d,LL num){
v[d]-=num;
if(v[d]==0)ans--;
} int notprime[N];
int prime[N],cnt;
int p[N];//p[i]表示i因数中最小的素数
int sn=1000;
int split[N][3];
int g[1007][1007]; int gcd(int x,int y){
int ans=1,i,d;
for(i=0;i<3;i++){
if(split[x][i]<=sn) d=g[split[x][i]][y%split[x][i]];
else d=(y%split[x][i]==0)?split[x][i]:1;
ans*=d;
y/=d;//避免算重
}
return ans;
} void init_gcd(){
notprime[1]=1;
int i,j,d;
for(i=2;i<N;i++){
if(!notprime[i]){
prime[++cnt]=i;
p[i]=i;
}
for(j=1;j<=cnt;j++){
if((LL)prime[j]*i>=N) break;
d=prime[j]*i;
notprime[d]=1;
p[d]=prime[j];
if(i%prime[j]==0) break;
}
} split[1][0]=split[1][1]=split[1][2]=1;
for(i=2;i<N;i++){
memcpy(split[i],split[i/p[i]],sizeof(split[i/p[i]]));
if(split[i][0]*p[i]<=sn) split[i][0]*=p[i];
else if(split[i][1]*p[i]<=sn) split[i][1]*=p[i];
else split[i][2]*=p[i];
} // gcd(0,0)=0 , gcd(0,x)=x
for(i=0;i<=sn;i++)
for(j=0;j<=i;j++){
if(!i||!j) g[i][j]=i|j;
else g[i][j]=g[j][i]=g[j][i%j];//j<=i
}
} struct node{
int fir,d;
}q[57];
int tq=0; bool cmp(node x,node y){
if(x.d==y.d) return x.fir<y.fir;
else return x.d<y.d;
} int uni(int num){
int i,cnt=0;
for(i=1;i<=num;i++)
if(q[i].d!=q[i-1].d) q[++cnt]=q[i];
return cnt;
} void init_ans(){
int i,j;
for(i=1;i<=n;i++){
for(j=1;j<=tq;j++) q[j].d=gcd(q[j].d,a[i]);
q[++tq].d=a[i];q[tq].fir=i;
sort(q+1,q+tq+1,cmp);
tq=uni(tq);
for(j=1;j<tq;j++) add(q[j].d,q[j+1].fir-q[j].fir);
add(q[tq].d,i+1-q[tq].fir);
}
} int all[M<<2]; void pushup(int x){
all[x]=gcd(all[x<<1],all[x<<1|1]);
} void build(int x,int l,int r){
if(l==r){
all[x]=a[l];
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
} void update(int x,int l,int r,int to){
if(l==r){
all[x]=a[l];
return;
}
int mid=l+r>>1;
if(to<=mid) update(x<<1,l,mid,to);
else update(x<<1|1,mid+1,r,to);
pushup(x);
} int getlf(int x,int l,int r,int tl,int tr,int G){
if(tl<=l&&r<=tr && all[x]%G==0) return 0;
if(l==r) return l;
int mid=l+r>>1,tp=0;
if(mid<tr) tp=getlf(x<<1|1,mid+1,r,tl,tr,G);
if(tl<=mid&&tp==0) tp=getlf(x<<1,l,mid,tl,tr,G);
return tp;
} int getrt(int x,int l,int r,int tl,int tr,int &G){
if(tl<=l&&r<=tr && all[x]%G==0) return n+1;
if(l==r) return l;
int mid=l+r>>1,tp=n+1;
if(tl<=mid) tp=getrt(x<<1,l,mid,tl,tr,G);
if(mid<tr&&tp==n+1) tp=getrt(x<<1|1,mid+1,r,tl,tr,G);
return tp;
} struct nnd{
int num,d;
nnd(int nn=0,int dd=0){num=nn;d=dd;}
}lf[57],rt[57];
int cl,cr; void mdf(int x,int kd){
int l,r,tp,nw,i,j;
cl=cr=0; l=1,r=x,nw=a[x];
while(l<=r){
tp=getlf(1,1,n,l,r,nw);
lf[++cl]=nnd(r-tp,nw);
if(tp!=0) nw=gcd(nw,a[tp]);
r=tp;
} l=x,r=n,nw=a[x];
while(l<=r){
tp=getrt(1,1,n,l,r,nw);
rt[++cr]=nnd(tp-l,nw);
if(tp!=n+1) nw=gcd(nw,a[tp]);
l=tp;
} for(i=1;i<=cl;i++)
for(j=1;j<=cr;j++){
if(kd==1) add(gcd(lf[i].d,rt[j].d),lf[i].num*rt[j].num);
else del(gcd(lf[i].d,rt[j].d),lf[i].num*rt[j].num);
}
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=rd(),m=rd();
int i,x,y;
for(i=1;i<=n;i++) a[i]=rd();
init_gcd();
init_ans();
build(1,1,n);
for(i=1;i<=m;i++){
x=rd(),y=rd();
mdf(x,-1);
a[x]=y;
update(1,1,n,x);
mdf(x,1);
printf("%d\n",ans);
}
return 0;
}

做法2

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cctype>
#include <algorithm>
typedef long long LL;
using namespace std;
const int M=50007;
const int N=57; inline int rd(){
int x=0;bool f=1;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
for(;isdigit(c);c=getchar()) x=x*10+c-48;
return f?x:-x;
} int n,m;
int val[M]; struct node{
int a[N];
}; node tl[M<<2],tr[M<<2];
int cl[M<<2],cr[M<<2],cp;
int all[M<<2]; LL num[1000007];
int ans; int gcd(int x,int y){
while(y){
x%=y;
swap(x,y);
}
return x;
} void add(int d){
if(num[d]==0) ans++;
num[d]++;
} void del(int d){
num[d]--;
if(num[d]==0) ans--;
} void eras(int x){
int lc=x<<1,rc=x<<1|1;
int i,j;
for(i=1;i<=cr[lc];i++)
for(j=1;j<=cl[rc];j++)
del(gcd(tr[lc].a[i],tl[rc].a[j]));
} void pushup(int x){
int lc=x<<1,rc=x<<1|1;
int i,j;
for(i=1;i<=cr[lc];i++)
for(j=1;j<=cl[rc];j++)
add(gcd(tr[lc].a[i],tl[rc].a[j])); all[x]=gcd(all[lc],all[rc]); cl[x]=0;
for(i=1;i<=cl[lc];i++) tl[x].a[++cl[x]]=tl[lc].a[i];
for(i=1;i<=cl[rc];i++) tl[x].a[++cl[x]]=gcd(all[lc],tl[rc].a[i]);
sort(tl[x].a+1,tl[x].a+cl[x]+1);
cl[x]=unique(tl[x].a+1,tl[x].a+cl[x]+1)-(tl[x].a+1); cr[x]=0;
for(i=1;i<=cr[rc];i++) tr[x].a[++cr[x]]=tr[rc].a[i];
for(i=1;i<=cr[lc];i++) tr[x].a[++cr[x]]=gcd(tr[lc].a[i],all[rc]);
sort(tr[x].a+1,tr[x].a+cr[x]+1);
cr[x]=unique(tr[x].a+1,tr[x].a+cr[x]+1)-(tr[x].a+1);
} void build(int x,int l,int r){
if(l==r){
tl[x].a[cl[x]=1]=val[l];
tr[x].a[cr[x]=1]=val[l];
all[x]=val[l];
add(val[l]);
return;
}
int mid=l+r>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
pushup(x);
} void chg(int x,int l,int r,int to){
if(l==r){
del(val[l]);
return;
}
int mid=l+r>>1;
if(to<=mid) chg(x<<1,l,mid,to);
else chg(x<<1|1,mid+1,r,to);
eras(x);
} void mdf(int x,int l,int r,int to){
if(l==r){
tl[x].a[cl[x]=1]=val[l];
tr[x].a[cr[x]=1]=val[l];
all[x]=val[l];
add(val[l]);
return;
}
int mid=l+r>>1;
if(to<=mid) mdf(x<<1,l,mid,to);
else mdf(x<<1|1,mid+1,r,to);
pushup(x);
} int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
int i,x,y;
n=rd(),m=rd();
for(i=1;i<=n;i++) val[i]=rd(); build(1,1,n); for(i=1;i<=m;i++){
x=rd(),y=rd();
chg(1,1,n,x);
val[x]=y;
mdf(1,1,n,x);
printf("%d\n",ans);
}
return 0;
}

hdu 5930 GCD 线段树上二分/ 强行合并维护信息的更多相关文章

  1. HDU 4747 Mex【线段树上二分+扫描线】

    [题意概述] 一个区间的Mex为这个区间没有出现过的最小自然数,现在给你一个序列,要求求出所有区间的Mex的和. [题解] 扫描线+线段树. 我们在线段树上维护从当前左端点开始的前缀Mex,显然从左到 ...

  2. LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分

    题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...

  3. 【BZOJ】4293: [PA2015]Siano 线段树上二分

    [题意]给定n棵高度初始为0的草,每天每棵草会长高a[i],m次收割,每次在d[i]天将所有>b[i]的草收割到b[i],求每次收割量.n<=500000. [算法]线段树上二分 [题解] ...

  4. 5.4 省选模拟赛 修改 线段树优化dp 线段树上二分

    LINK:修改 题面就不放了 大致说一下做法.不愧是dls出的题 以前没见过这种类型的 不过还是自己dp的时候写丑了. 从这道题中得到一个结论 dp方程要写的优美一点 不过写的过丑 优化都优化不了. ...

  5. 贪心+离散化+线段树上二分。。。 Samara University ACM ICPC 2016-2017 Quarterfinal Qualification Contest G. Of Zorcs and Axes

    题目链接:http://codeforces.com/gym/101149/problem/G 题目大意:给你n对数字,为(a[i], b[i]),给你m对数字,为(w[i], c[i]).给n对数字 ...

  6. [NOIP2015模拟10.27] [JZOJ4270] 魔道研究 解题报告(动态开点+权值线段树上二分)

    Description “我希望能使用更多的魔法.不对,是预定能使用啦.最终我要被大家称呼为大魔法使.为此我决定不惜一切努力.”——<The Grimoire of Marisa>雾雨魔理 ...

  7. 【洛谷5537】【XR-3】系统设计(哈希_线段树上二分)

    我好像国赛以后就再也没有写过 OI 相关的博客 qwq Upd: 这篇博客是 NOIP (现在叫 CSP 了)之前写的,但是咕到 CSP 以后快一个月才发表 -- 我最近这么咕怎么办啊 -- 题目 洛 ...

  8. 9 16 模拟赛&关于线段树上二分总结

    1 考试时又犯了一个致命的错误,没有去思考T2的正解而是去简单的推了一下式子开始了漫漫找规律之路,不应该这样做的 为了得到规律虽然也打了暴力 但是还是打了一些不必要的程序 例如求组合数什么的比较浪费时 ...

  9. CF 1405E Fixed Point Removal【线段树上二分】

    CF 1405E Fixed Point Removal[线段树上二分]  题意: 给定长度为\(n\)的序列\(A\),每次操作可以把\(A_i = i\)(即值等于其下标)的数删掉,然后剩下的数组 ...

随机推荐

  1. 谭浩强 c++程序设计第一章课后习题 第10题

    #include <iostream> using namespace std; int main() { int a,b,c; cout<<"请输入三个整数类型的数 ...

  2. zabbix告警时间和恢复时间相同的解决方法

    出现原因:在动作,恢复操作中,恢复时间成了{EVENT.DATE} {EVENT.TIME},所以和告警时间相同. 解决方法:将{EVENT.DATE}{EVENT.TIME}改成{EVENT.DAT ...

  3. Linux Shell 几个特殊符号命令 & 、&& 、 ||

    & 放在启动参数后面表示设置此进程为后台进程 默认情况下,进程是前台进程,这时就把Shell给占据了,我们无法进行其他操作,对于那些没有交互的进程,很多时候,我们希望将其在后台启动,可以在启动 ...

  4. 数据结构(C语言)分享笔记:数据结构的逻辑层次、存储层次

    [1] 严格意义上数据结构的概念 数据结构,一个简单的定义:相互之间存在一种或多种特定关系的数据元素的集合.即:数据结构 = 元素集合 + 元素间关系的集合 . 在讨论数据结构时,可以基于两个不同的层 ...

  5. jsp引用servlet生成的验证码代码演示

    此演示代码主要包括以下三部分:1.checkCode.java:用于生成验证码2.checkCodeServler3.check.jsp 验证 下面是checkCode.java的内容: 复制代码代码 ...

  6. git Bash 学习

    ,ranh新建一个本地仓库并与github连接的方法 注:该终端也具有按tab键补全功能,应该合理应用 1. 新建一个文件夹,并将git bash的位置转到相应文件夹下(cd 命令转移) 2.git ...

  7. forEach 以及 IE兼容

      语法 array.forEach(function(currentValue, index, arr), thisValue) 参数 参数 描述 function(currentValue, in ...

  8. 本地已经存在的项目如何跟github发生关联

    切换到本地项目地址 git init 初始化项目.该步骤会创建一个 .git文件夹是附属于该仓库的工作树. git add . git commit -am 'initial commit' git ...

  9. django之模型层

    1. ORM MVC或者MTV框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员 ...

  10. readhat7.0 bond配置

    Bonding的模式一共有7种: 1.mode=0(balance-rr)(平衡抡循环策略) 概念:链路负载均衡,增加带宽,支持容错,一条链路故障会自动切换正常链路.交换机需要配置聚合口,思科叫por ...