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. Oracle 函数 之 wm_concat()

    wm_concat() 把列转换成一行一列显示,使用wm_concat函数可以显示在一行一列. --1 建表 create table province_city ( province varchar ...

  2. 51nod——1285 山峰和分段(暴力出奇迹)

    要求每段的点数都一样,因此分的段数cnt肯定是n的因子,要求每段都有山峰,因此cnt肯定小于等于山峰数量.分段的宽度d=n/cnt,对山峰数量做一个前缀和,检查一下每一段的山峰数量是否没有增加即可. ...

  3. Win 无法安装 python 包

    Win 上使用 pip install 安装出错 使用 wheel 安装 pip install wheel 下载 编译包 http://www.lfd.uci.edu/~gohlke/pythonl ...

  4. LAMP PHP 详解

    目录 LAMP PHP 详解 LAMP 请求流程与原理 PHP 简介 PHP Zend Engine Opcode php 配置详解 php 加速器 部署LAMP 使用 php 连接 mysql 最基 ...

  5. http 工作模式与模块

    目录 http 工作模式与模块 http 服务器应用 MPM工作模式 prefork worker event 进程角色 httpd功能特性 http 安装 centos6配置目录 http 2.2 ...

  6. DevOps - 配置管理 - Puppet

    uppet总结 一.基础知识 1. Puppet是开源的基于Ruby的系统配置管理工具,依赖于C/S的部署架构.Puppet这样的自动化配置管理工具可以帮助系统管理员更加方便的完成多台服务器的升级软件 ...

  7. 快速搭建FTP服务

    Linux下ftp服务可以通过搭建vsftpd服务来实现,以CentOS为例,首先查看系统中是否安装了vsftpd,可以通过执行命令 rpm -qa | grep vsftpd 来查看是否安装相应的包 ...

  8. 小程序wafer2操作数据库

    小程序操作数据库 //小程序控制台phpmyadmin里给数据库cAuth添加表 //controllers/hello.js const { mysql } = require('../qcloud ...

  9. h5获取摄像头拍照功能

    完整代码展示 <!DOCTYPE html> <head> <title>HTML5 GetUserMedia Demo</title> <met ...

  10. python模块汇总练习

    模块练习 1.random模块 # print(random.random()) # print(random.randint(1,3)) #模拟随机验证码 def make_code(n=5): r ...