zoukankan      html  css  js  c++  java
  • 「LibreOJ NOI Round #2」签到游戏

    题目

    瞎猜一下我们只要(n)次询问就能确定出({A_i})

    感受一下大概是询问的区间越长代价就越小,比如询问([l,n])([1,r])的代价肯定不会超过([l,r])

    所以大胆猜一下我们询问的只有一些前缀和后缀

    首先我们肯定要询问一下([1,n])的和,之后我们考虑顺次得到(A_1)(A_n)的和

    想得到(A_1),我们当然可以直接询问([1,1]),但是有([1,n])的和我们询问([2,n])的和也能得到(A_1)

    同理我们想得到(A_i),我们可以直接询问([1,i])的和,由于这个时候(A_1)(A_{i-1})的和都知道了,做一个差就能得到(A_i)的和;也可以询问([i+1,n]),也能得到(A_i)的和

    于是我们维护一个前缀、后缀的(gcd)显然哪个小选哪一个

    这样复杂度是(O(qnlog n))

    考虑到一个序列的前后缀(gcd)之会变化(log)次,于是我们可以考虑用线段树来维护区间(gcd),二分出每次变化的位置,一共要二分(log)次,每次二分有一个(log),查询前缀(gcd)(log^2),四个(log)显然啥都过不去

    首先求区间(gcd)就是没有必要的,我们只需要对于二分出来的这段区间查询一下这段区间里是否所有数都是当前前缀(gcd)的倍数就好了,只有全都都是当前前缀(gcd)的倍数前缀(gcd)才不会发生变化,于是线段树上的查询变成了(log n)

    再来考虑有线段树为什么还要生硬地套一个二分,我们直接在线段树上二分即可,对于一个当前的端点(l),我们直接在线段树上查([l,n])这段区间,([l,n])在线段树上被分成了(log)段区间,我们依次访问到这些区间;当我们访问到一段区间的时候,发现如果这个区间的(gcd)不是当前前缀(gcd)的倍数,那么说明这个变化点肯定在这个区间内,我们直接在这颗子树里二分;否则我们再去访问接下来的区间,这样复杂度是两个(log)相加

    于是整体的复杂度就是(O(qlog^2n))

    代码

    #include<bits/stdc++.h>
    #define re register
    #define LL long long
    inline int read() {
    	char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
    }
    const int maxn=1e5+5;
    inline int max(int a,int b) {return a>b?a:b;}
    inline int min(int a,int b) {return a<b?a:b;}
    int gcd(int a,int b) {return !b?a:gcd(b,a%b);}
    struct Seg{int l,r,v;}b[2][105];
    int n,Q,a[maxn];
    int l[maxn<<2],r[maxn<<2],g[maxn<<2],pos[maxn];
    inline void pushup(int i) {g[i]=gcd(g[i<<1],g[i<<1|1]);}
    void build(int x,int y,int i) {
    	l[i]=x,r[i]=y;
    	if(x==y) {pos[x]=i;g[i]=a[x];return;}
    	int mid=x+y>>1;
    	build(x,mid,i<<1),build(mid+1,y,i<<1|1);
    	pushup(i);
    }
    void change(int x,int v) {
    	a[x]=v;x=pos[x];g[x]=v;x>>=1;
    	while(x) pushup(x),x>>=1;
    }
    int chk(int i,int v) {
    	if(l[i]==r[i]) return g[i]%v==0?l[i]:l[i]-1;
    	if(g[i<<1]%v==0) return chk(i<<1|1,v);
    	return chk(i<<1,v);
    }
    int find(int x,int y,int i,int v) {
    	if(x>r[i]||y<l[i]) return 0;
    	if(x<=l[i]&&y>=r[i]) {
    		if(g[i]%v==0) return r[i];
    		return chk(i,v);
    	}
    	int mid=l[i]+r[i]>>1;
    	int now=find(x,y,i<<1,v);
    	if(now!=mid&&now) return now;
    	return max(now,find(x,y,i<<1|1,v));
    }
    int calc(int i,int v) {
    	if(l[i]==r[i]) return g[i]%v==0?l[i]:l[i]+1;
    	if(g[i<<1|1]%v==0) return calc(i<<1,v);
    	return calc(i<<1|1,v);
    }
    int query(int x,int y,int i,int v) {
    	if(x>r[i]||y<l[i]) return 0;
    	if(x<=l[i]&&y>=r[i]) {
    		if(g[i]%v==0) return l[i];
    		return calc(i,v);
    	}
    	int mid=l[i]+r[i]>>1;
    	int now=query(x,y,i<<1|1,v);
    	if(now&&now!=mid+1) return now;
    	int t=query(x,y,i<<1,v);
    	if(t) return t;return now;
    }
    int main() {
    	n=read(),Q=read();
    	for(re int i=1;i<=n;i++) a[i]=read();
    	build(1,n,1);int x,v,tot,cnt,now,p;
    	while(Q--) {
    		x=read(),v=read();change(x,v);
    		tot=0;b[0][++tot].l=1;
    		while(b[0][tot].l<=n) {
    			b[0][tot].v=gcd(b[0][tot-1].v,a[b[0][tot].l]);
    			b[0][tot].r=find(b[0][tot].l,n,1,b[0][tot].v);++tot;
    			b[0][tot].l=b[0][tot-1].r+1;
    		}
    		--tot;
    		cnt=0;b[1][++cnt].r=n;
    		while(b[1][cnt].r>=1) {
    			b[1][cnt].v=gcd(b[1][cnt-1].v,a[b[1][cnt].r]);
    			b[1][cnt].l=query(1,b[1][cnt].r,1,b[1][cnt].v);++cnt;
    			b[1][cnt].r=b[1][cnt-1].l-1;
    		}
    		--cnt;now=1;p=1;
    		for(re int i=1;i<=cnt;i++) b[1][i].l--,b[1][i].r--;
    		LL ans=0;
    		while(p<n) {
    			while(b[0][now].r<p) now++;
    			while(b[1][cnt].r<p) --cnt;
    			ans+=1ll*min(b[0][now].v,b[1][cnt].v)*(min(b[0][now].r,b[1][cnt].r)-p+1);
    			p=min(b[0][now].r,b[1][cnt].r)+1;
    		}
    		printf("%lld
    ",ans+b[0][tot].v);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Codeforces 1355 D. Game With Array
    Codeforces 1355 C. Count Triangles
    Codeforces Round #641 (Div. 2)
    Codeforces 1345 D
    hdu6787(骰子+往回推的传输带问通过方案,dp)
    hdu6745(渐减1过程的最多互质对数,dp)
    D Points Construction Problem(思维构造,等周定理)
    Codeforces Round #658 (Div. 2)D(01背包)
    Codeforces Round #655 (Div. 2)E(矩阵范围dp)
    hdu4622([u,v]内有多少个子串)
  • 原文地址:https://www.cnblogs.com/asuldb/p/11543148.html
Copyright © 2011-2022 走看看