[COCI2017-2018#2] ​​Garaža

维护一个 n n 个数的序列支持两个操作。
1. Change​ ​the​ ​value​ ​at​ ​position​X​ ​in​ ​the​ ​sequence​ ​to​ V ​ ​ V
2. Determine the number of interesting contiguous subarrays contained in the interval [L,R] [ ​ L , ​ ​ ​ R ] ​ ​of​ ​the​ ​sequence.

操作 1 1 :单点修改元素权值
操作2 : 查询区间 [l,r] [ l , r ] 内有趣的子串的个数。(有趣的子串指 串内所有元素的最大公约数大于1)

n,m<=1054s n , m <= 10 5 时 限 4 s

emm这题对于我这种菜鸡来说是挺好的。
官方题解的思路顺序有些毒瘤….我就说一下我的理解吧
我们考虑直接线段树维护区间内答案的个数。 l+r l + r 区间内的答案为 l l 的答案 加上 r 的答案再加上从左区间出发,到达右区间的串的数量。
我们考虑如何维护跨区间答案的数量。
一个子串对答案有贡献,当且仅当串的 GCD G C D 大于 1 1 。问题转化为维护跨区间的 GCD 大于 1 1 的有多少个串.
线段树是无法维护跨区间的数据的,对于这种东西,我们可以拆成前缀+后缀的形式。
记录每个区间的前缀GCD 与 后缀 GCD G C D ,
打表或思考发现 两个数的 GCD G C D 不超过最大数的 1/2 1 / 2 ,且前缀 GCD G C D 单调递减。
所以前缀 GCD G C D 的种类不超过 log l o g 个,我们可以开 pair p a i r 记录前缀 GCD G C D 的值与数量。
整个区间的前缀 GCD G C D 长度不超过 L L 区间的部分直接继承过来。
超过 L 的部分 就是 L L 整段区间的 GCD R R 中前缀的 GCD GCD G C D .
如果前缀 GCD G C D 相同,直接数量加到上一个上。
如果前缀 GCD G C D 不同,新建一个 pair p a i r 数值为算出来的 GCD G C D . 数量为 R R 区间前缀的 GCD 的数量。
后缀 GCD G C D 同理。

然后我们得到合并区间计算答案时,枚举 L L 区间的后缀与 R 区间的前缀。我们发现右移 L L 区间指针时,其实是去掉了一个数的GCD,所以 R R 区间的指针也会右移或不动。所以计算时 L R R 区间的指针都是单调右移的。
所以可以 twopointer 快速得到答案.

然后注意合并区间时代码的细节。。一定要对拍。。

主要就是
线段树的维护前后缀维护跨区间信息,观察 GCD G C D 性质发现只有 log l o g 种不同 GCD G C D twopointer t w o − p o i n t e r 快速得到答案

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef pair<ll,ll>par;
#define mp make_pair
#define lson (o<<1)
#define rson (o<<1|1)
const ll MAXN=2e5+5;

inline ll gcd(ll a,ll b){
    return b==0?a:gcd(b,a%b);
}

inline ll read(){
    ll x=0;char c=getchar();
    while(c<'0'||c>'9'){c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x;
}

ll a[MAXN];

struct xds{
    vector<par>pre,sub;
    ll iting,lm,rm;
}n[MAXN<<2];

inline xds pushup(xds l,xds r){
    xds node;
    node.lm=l.lm;node.rm=node.rm;
    node.pre.clear();node.sub.clear();
    for(ll i=0;i<l.pre.size();i++)node.pre.push_back(l.pre[i]);
    for(ll i=l.pre.size();i<l.pre.size()+r.pre.size();i++){
        ll tem=gcd(l.pre[l.pre.size()-1].first,r.pre[i-l.pre.size()].first);
        if(node.pre[node.pre.size()-1].first==tem)node.pre[node.pre.size()-1].second+=r.pre[i-l.pre.size()].second;
        else node.pre.push_back(mp(tem,r.pre[i-l.pre.size()].second));
    }
    for(ll i=0;i<r.sub.size();i++)node.sub.push_back(r.sub[i]);
    for(ll i=r.sub.size();i<l.sub.size()+r.sub.size();i++){
        ll tem=gcd(r.sub[r.sub.size()-1].first,l.sub[i-r.sub.size()].first);
        if(node.sub[node.sub.size()-1].first==tem)node.sub[node.sub.size()-1].second+=l.sub[i-r.sub.size()].second;
        else node.sub.push_back(mp(tem,l.sub[i-r.sub.size()].second));
    }
    node.iting=l.iting+r.iting;
    ll polll=l.sub.size()-1,pollr=0,len=l.sub[0].second,lw=0,flag=0;
    while(1){
        //int tmp=(pollr==r.pre.size())?pollr-1:pollr;
    //  cout<<tmp<<"fafafa"<<endl;
        int tmp=flag?pollr-1:pollr;
        while(polll>=0&&gcd(l.sub[polll].first,r.pre[tmp].first)==1)polll--,flag=0;
        if(polll<0)break;if(flag)polll--;
        len=l.sub[polll].second;
        while(pollr+1<=r.pre.size()&&gcd(r.pre[pollr].first,l.sub[polll].first)!=1)
            lw+=r.pre[pollr].second,pollr++;    
            //cout<<node.iting<<" "<<len<<" qwq "<<lw<<" "<<l.sub[polll].first<<" "<<polll<<endl;
        if(gcd(l.sub[polll].first,r.pre[pollr-1].first)!=1)node.iting+=len*lw;
        if(polll<=0)break;
        flag=1;
    }
    return node;
}
void build(ll o,ll l,ll r){
    if(l==r){
        n[o].pre.push_back(mp(a[l],1));
        n[o].sub.push_back(mp(a[l],1));
        n[o].iting=a[l]==1?0:1;
        n[o].lm=n[o].rm=l;
        return;
    }
    ll mid=l+r>>1;
    build(lson,l,mid);build(rson,mid+1,r);
    n[o]=pushup(n[lson],n[rson]);
}
inline void change(ll o,ll l,ll r,ll pos,ll val){
    if(l==r){
        n[o].pre.clear();n[o].sub.clear();
        n[o].pre.push_back(mp(val,1));
        n[o].sub.push_back(mp(val,1));
        n[o].iting=val==1?0:1;
        return;
    }
    ll mid=l+r>>1;
    if(pos<=mid)change(lson,l,mid,pos,val);
    else change(rson,mid+1,r,pos,val);
    n[o]=pushup(n[lson],n[rson]);
}
inline xds query(ll o,ll l,ll r,ll ql,ll qr){
    if(ql<=l&&qr>=r)return n[o];
    ll mid=l+r>>1,ans=0;
    xds a,b;bool flagl=0,flagr=0;
    if(ql<=mid)a=query(lson,l,mid,ql,qr),flagl=1;
    if(qr>mid)b=query(rson,mid+1,r,ql,qr),flagr=1;
    if(flagl==1&&flagr==1){
        return pushup(a,b);
    }
    else if(flagl)return a;
    return b;
}

ll tot,m;

int main(){
//  freopen("garaza.in.1a","r",stdin);
//  freopen("garaza.out","w",stdout);
    tot=read();m=read();
    for(ll i=1;i<=tot;i++){
        a[i]=read();
    }   
    build(1,1,tot); 
    for(ll i=1;i<=m;i++){
        ll opt,l,r;
        opt=read(),l=read(),r=read();
        if(opt==1)change(1,1,tot,l,r);
        else cout<<query(1,1,tot,l,r).iting<<endl;
    }   
/*  for(int i=1;i<=3;i++){
        cout<<i<<":"<<endl;
        for(int j=0;j<n[i].pre.size();j++)cout<<n[i].pre[j].first<<" "<<n[i].pre[j].second<<endl;
        cout<<endl;
        for(int j=0;j<n[i].sub.size();j++)cout<<n[i].sub[j].first<<" "<<n[i].sub[j].second<<endl;
        cout<<endl;
    }*/
}
/*
13 3
23729 21451 5233 4434 3109 32116 8606 13454 1636 5660 10169 32582 10081 
2 1 13
2 1 7
2 8 13
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值