zoukankan      html  css  js  c++  java
  • Loj#576. 「LibreOJ NOI Round #2」签到游戏(前缀和转化+最小生成树+贪心+gcd性质+线段树)

    https://loj.ac/problem/576

    题解:

    考虑询问了区间([l,r]),就知道了和,也就知道了(s[r])(s[l-1])的差。

    那么给(r)(l-1)连一条边,我们相当于要搞出点为(0..n)的最小生成树。

    运用kruskal的想法,每次找最小的连接不同联通块的边,发现一定是一个前缀或者后缀是最优的。

    所以考虑找到一个最右的分界点(k)满足(gcd(b[1..k])ge gcd(b[k+1..n])),那么(x le k)的点都是选择(gcd(b[x+1..n])),而(> k)的点都是(gcd(b[1..x]))

    我们知道前缀gcd的变化点只有log个,考虑怎么找出来。

    不难想到线段树维护区间(gcd),然后每次线段树二分,直接搞复杂度是(O(n~log^3~n)),注意判断一个区间是否合法时只用看这个区间的(gcd)是不是我要的(gcd)的倍数,即可省掉一个(log)

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 1e5 + 5;
    
    int n, Q, b[N], x, y;
    
    int gcd(int x, int y) { return !y ? x : gcd(y, x % y);}
    
    int t[N * 4];
    
    #define i0 i + i
    #define i1 i + i + 1
    void bt(int i, int x, int y) {
    	if(x == y) {
    		t[i] = b[x];
    		return;
    	}
    	int m = x + y >> 1;
    	bt(i0, x, m); bt(i1, m + 1, y);
    	t[i] = gcd(t[i0], t[i1]);
    }
    
    int pl, pr;
    
    void add(int i, int x, int y) {
    	if(x == y) {
    		t[i] = b[x];
    		return;
    	}
    	int m = x + y >> 1;
    	if(pl <= m) add(i0, x, m); else add(i1, m + 1, y);
    	t[i] = gcd(t[i0], t[i1]);
    }
    
    struct P {
    	int x, y;
    	P(int _x = 0, int _y = 0) {
    		x = _x, y = _y;
    	}
    };
    
    int px, py, pz;
    void ef(int i, int x, int y) {
    	if(y < pl || x > pr) return;
    	if(t[i] % py == 0) return;
    	if(x == y) { px = x; return;}
    	int m = x + y >> 1;
    	if(!pz) {
    		ef(i0, x, m);
    		if(px == -1) ef(i1, m + 1, y);
    	} else {
    		ef(i1, m + 1, y);
    		if(px == -1) ef(i0, x, m);
    	}
    }
    
    P p[N], q[N]; int p0, q0;
    
    void work();
    void Qry() {
    	p0 = 0;
    	for(int i = 1, s = 0; i <= n; i ++) {
    		s = gcd(s, b[i]);
    		pl = i, pr = n;
    		px = -1, py = s; pz = 0;
    		ef(1, 1, n);
    		if(px == -1) px = n + 1;
    		px --;
    		p[++ p0] = P(px, s);
    		i = px;
    	}
    	
    	q0 = 0;
    	for(int i = n, s = 0; i > 0; i --) {
    		s = gcd(s, b[i]);
    		pl = 1, pr = i;
    		px = -1, py = s; pz = 1;
    		ef(1, 1, n);
    		if(px == -1) px = 0;
    		px ++;
    		q[++ q0] = P(px, s);
    		i = px;
    	}
    	
    	p[p0 + 1].x = n + 1;
    	q[q0 + 1].x = 0;
    	q[0].x = n + 1;
    	
    	work();
    }
    
    int qry1(int x) {
    	fo(i, 1, p0) if(p[i].x >= x)
    		return p[i].y;
    }
    
    int qry2(int x) {
    	if(x > n) return 1e9 + 1;
    	fo(i, 1, q0) if(q[i].x <= x)
    		return q[i].y;
    }
    
    int pd(int k) {
    	if(k < 0) return 0;
    	int v1 = qry1(k), v2 = qry2(k + 1);
    	return v1 >= v2;
    }
    
    ll sum1(int k) {
    	ll ans = 0;
    	fo(i, 1, p0) {
    		int x = p[i - 1].x + 1, y = p[i].x;
    		if(y >= k)  ans += (ll) p[i].y * (y - max(x, k) + 1);
    	}
    	return ans;
    }
    
    ll sum2(int k) {
    	ll ans = 0;
    	fo(i, 1, q0) {
    		int x = q[i].x, y = q[i - 1].x - 1;
    		if(x <= k) ans += (ll) q[i].y * (min(y, k) - x + 1);
    	}
    	return ans;
    }
    
    void work() {
    	int k = 0;
    	fo(i, 1, p0) {
    		if(pd(p[i].x)) k = max(k, p[i].x);
    		if(pd(p[i - 1].x + 1)) k = max(k, p[i - 1].x + 1);
    	}
    	fo(i, 1, q0) {
    		if(pd(q[i].x - 1)) k = max(k, q[i].x - 1);
    		if(pd(q[i - 1].x - 2)) k = max(k, q[i - 1].x - 2);
    	}
    	ll ans = sum2(k + 1) + sum1(k + 1);
    	ans -= p[p0].y;
    	pp("%lld
    ", ans);
    }
    
    int main() {
    	scanf("%d %d", &n, &Q);
    	fo(i, 1, n) scanf("%d", &b[i]);
    	bt(1, 1, n);
    	fo(ii, 1, Q) {
    		scanf("%d %d", &x, &y);
    		b[x] = y;
    		pl = pr = x;
    		add(1, 1, n);
    		Qry();
    	}
    }
    
  • 相关阅读:
    AGC/ARC
    日常训练
    SQL经典问题四表查询(教师,学生,成绩,课程表)---MySQL版
    15个最佳的 JavaScript 表单验证库
    推荐6个国内技术大牛博客,全栈工程师修行的秘籍!(建议收藏)
    java-linux安装和配置
    vue学习笔记
    JVM学习笔记
    SpringMVC学习笔记
    Mybatis学习笔记
  • 原文地址:https://www.cnblogs.com/coldchair/p/13413812.html
Copyright © 2011-2022 走看看