Myhchael原创题系列 Mychael vs Kid 【题解】

题目链接

Mychael vs Kid

题解

先说说这题的由来及前身

前身

首先有一个很经典的题目:

维护区间加,查询区间\(gcd\)

如果强行用线段树维护的话,区间加之后就没法直接确定当前区间的\(gcd\),不可直接维护

这个时候就用到了\(gcd\)的一个性质:

\[(a,b) = (a - b,b)\]

三个的\(gcd\)也是符合的:

\[(a,b,c) = (a,b,c - b) = (a,b - a,c - b)\]

同样可以推广出\(n\)个的情况

\[gcd\{a_i\} = gcd(a_1,gcd\{a_j - a_{j - 1}\}) \qquad i \in [1,n] \quad j \in [2,n]\]

也就是说,区间差分了之后,我们要求原区间\(gcd[l,r]\),就相当于求\(a_l\)和差分后区间\([l + 1,r]\)的\(gcd\)

所以我们只需维护差分区间和区间每个位置的值即可

维护区间值用线段树一点问题都没有

在加法下维护差分区间,一个区间加上一个数,仅影响区间端点的值,所以单点修改即可

所以我们开两棵线段树,一棵维护权值,一棵维护差值的\(gcd\),即可\(O(nlog^2n)\)解决这道题

本题

本题多加入了一个区间乘法操作

本来想\(yy\)分块的,可后来发现线段树依旧可写并且暴艹分块

思考一下发现,区间乘法后,区间差分值也乘上那个数,对应\(gcd\)也乘上一个数,可以使用线段树维护

至于端点的差值改变,只需取出端点的值计算出差值后单点加法修改即可

复杂度依旧是\(O(nlog^2n)\)

部分分

前\(5\%\),咳,,,

对于\(n,m \le 300\),暴力求即可

对于没有操作\(2\)的,就是原版题目

对于\(n,m \le 3 \times 10^4\)的,可以考虑分块

std

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
#define ls (u << 1)
#define rs (u << 1 | 1)
using namespace std;
const int maxn = 100005,maxm = 100005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
LL n,m,A[maxn],D[maxn];
LL gcd(LL a,LL b){
    if (a < 0) a = -a;
    if (b < 0) b = -b;
    return b ? gcd(b,a % b) : a;
}
struct Seg_Gcd{
    LL Gcd[maxn << 2],tag[maxn << 2];
    void upd(int u){
        Gcd[u] = gcd(Gcd[ls],Gcd[rs]);
    }
    void pd(int u){
        if (tag[u] != 1){
            Gcd[ls] *= tag[u]; tag[ls] *= tag[u];
            Gcd[rs] *= tag[u]; tag[rs] *= tag[u];
            tag[u] = 1;
        }
    }
    void build(int u,int l,int r){
        tag[u] = 1;
        if (l == r){
            Gcd[u] = D[l];
            return;
        }
        int mid = l + r >> 1;
        build(ls,l,mid);
        build(rs,mid + 1,r);
        upd(u);
    }
    void Add(int u,int l,int r,int pos,int v){
        if (l == r){Gcd[u] += v; return;}
        pd(u);
        int mid = l + r >> 1;
        if (mid >= pos) Add(ls,l,mid,pos,v);
        else Add(rs,mid + 1,r,pos,v);
        upd(u);
    }
    void Mult(int u,int l,int r,int L,int R,int v){
        if (l >= L && r <= R){Gcd[u] *= v; tag[u] *= v; return;}
        pd(u);
        int mid = l + r >> 1;
        if (mid >= L) Mult(ls,l,mid,L,R,v);
        if (mid < R) Mult(rs,mid + 1,r,L,R,v);
        upd(u);
    }
    LL query(int u,int l,int r,int L,int R){
        if (l >= L && r <= R) return Gcd[u];
        pd(u);
        int mid = l + r >> 1;
        if (mid >= R) return query(ls,l,mid,L,R);
        if (mid < L) return query(rs,mid + 1,r,L,R);
        return gcd(query(ls,l,mid,L,R),query(rs,mid + 1,r,L,R));
    }
}T2;
struct Seg{
    LL val[maxn << 2],mult[maxn << 2],add[maxn << 2];
    void pd(int u){
        if (mult[u] != 1){
            val[ls] *= mult[u]; mult[ls] *= mult[u]; add[ls] *= mult[u];
            val[rs] *= mult[u]; mult[rs] *= mult[u]; add[rs] *= mult[u];
            mult[u] = 1;
        }
        if (add[u]){
            val[ls] += add[u]; add[ls] += add[u];
            val[rs] += add[u]; add[rs] += add[u];
            add[u] = 0;
        }
    }
    void build(int u,int l,int r){
        add[u] = 0; mult[u] = 1;
        if (l == r){
            val[u] = A[l];
            return;
        }
        int mid = l + r >> 1;
        build(ls,l,mid);
        build(rs,mid + 1,r);
    }
    void Add(int u,int l,int r,int L,int R,int v){
        if (l >= L && r <= R){val[u] += v; add[u] += v; return;}
        pd(u);
        int mid = l + r >> 1;
        if (mid >= L) Add(ls,l,mid,L,R,v);
        if (mid < R) Add(rs,mid + 1,r,L,R,v);
    }
    void Mult(int u,int l,int r,int L,int R,int v){
        if (l >= L && r <= R){val[u] *= v; add[u] *= v; mult[u] *= v; return;}
        pd(u);
        int mid = l + r >> 1;
        if (mid >= L) Mult(ls,l,mid,L,R,v);
        if (mid < R) Mult(rs,mid + 1,r,L,R,v);
    }
    LL query(int u,int l,int r,int pos){
        if (l == r) return val[u];
        pd(u);
        int mid = l + r >> 1;
        if (mid >= pos) return query(ls,l,mid,pos);
        return query(rs,mid + 1,r,pos);
    }
}T1;
int main(){
    n = read(); m = read();
    for (int i = 1; i <= n; i++){
        A[i] = read();
        D[i] = A[i] - A[i - 1];
    }
    T1.build(1,1,n);
    T2.build(1,1,n);
    LL opt,l,r,v,t1,t2;
    while (m--){
        opt = read(); l = read(); r = read();
        if (opt == 1){
            v = read();
            T1.Add(1,1,n,l,r,v);
            T2.Add(1,1,n,l,v);
            if (r + 1 <= n) T2.Add(1,1,n,r + 1,-v);
        }
        else if (opt == 2){
            v = read();
            t1 = T1.query(1,1,n,l);
            if (r + 1 <= n) t2 = T1.query(1,1,n,r);
            if (l < r) T2.Mult(1,1,n,l + 1,r,v);
            T2.Add(1,1,n,l,t1 * v - t1);
            if (r + 1 <= n) T2.Add(1,1,n,r + 1,t2 - t2 * v);
            T1.Mult(1,1,n,l,r,v);
        }
        else {
            if (l < r) printf("%lld\n",gcd(T1.query(1,1,n,l),T2.query(1,1,n,l + 1,r)));
            else printf("%lld\n",T1.query(1,1,n,l));
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Mychael/p/9102025.html

时间: 2024-08-07 16:06:11

Myhchael原创题系列 Mychael vs Kid 【题解】的相关文章

微软100题系列之-----设计包含min函数的栈

题意: 定义栈的数据结构,要求添加一个min 函数,能够得到栈的最小元素. 要求函数min.push 以及pop 的时间复杂度都是O(1). 思路:定义两个栈,一个用来记录数据的插入和删除,一个用来存储最小值的变化 代码如下: template <class T> class Stack { public: Stack(int len=100); T Min(); T Pop(); void Push(T val); private: T top1,top2; T *stack1,*stack

NOI题库 1768最大子矩阵 题解

NOI题库 1768最大子矩阵  题解 总时间限制: 1000ms 内存限制: 65536kB 描述 已知矩阵的大小定义为矩阵中所有元素的和.给定一个矩阵,你的任务是找到最大的非空(大小至少是1 * 1)子矩阵. 比如,如下4 * 4的矩阵 0 -2 -7 0 9 2 -6 2 -4 1 -4 1 -1 8 0 -2 的最大子矩阵是 9 2 -4 1 -1 8 这个子矩阵的大小是15. 输入   输入是一个N * N的矩阵.输入的第一行给出N (0 < N <= 100).再后面的若干行中,依

软考培训 | 根据2016新教程改版的百题系列完成改版工作

攻克要塞之考前100题系列完成改变工作. 两本书籍,<攻克要塞百题系列 系统集成项目管理工程师考前冲刺100题 第二版> <攻克要塞百题系列 信息系统项目管理师考前冲刺100题> 其中,中级系列用书根据2016新版教程和考纲进行了内容调整. 预计9月底上市发行! 本书配套的视频将于下半年在51cto视频网站发行.

微软面试100题系列算法心得

微软100题系列地址 答案地址 谓之随笔,当是自己在练习此类算法的一些想法,一些心得,一些领悟,一些借鉴,当自引用之时,会附上相应的链接! 题:把二元查找树转变成排序的双向链表(树) 描述:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 思维过程[个人思维]: 1. 二元查找树是指在任何结点看来,它的左子树上的值要少于当前结点的值,而它的右子树上的值要大于当前结点的值,对于等于的值那就看自己的原则放左子树还是右子树. 2. 关于树的算法必

hdu5017:补题系列之西安网络赛1011

补题系列之西安网络赛1011 题目大意:给定一个椭球: 求它到原点的最短距离. 思路: 对于一个椭球的标准方程 x^2/a^2 + y^2/b^2 +z^2/c^2=1 来说,它到原点的最短距离即为min(a,b,c) 所以我们需要把原方程化为标准型. 这时候线代就排上用场了,注意到原方程是一个二次型. 化为标准型 1/(k1)*x^2+1/(k2)*y^2+1/(k3)*z^2=1 后  min(k1,k2,k3)即为答案 而这里的1/k1,1/k2,1/k3 就是二次型矩阵的特征值 如何求特

ACdream oj C - 神奇的%系列一 (水题系列--略坑)

 C - 神奇的%系列一 Time Limit: 6000/3000 MS (Java/Others)      Memory Limit: 65536/32768 KB (Java/Others) Submit Status Problem Description 在计算机的世界里,%不是百分比,而是除法取余哟! 比如: 4 % 2 = 0 5 % 3 = 2 给你 2<=N<=100000 个数,a[1],a[2]...a[i]...a[n]. 其中:1<=a[i]<=10

nyoj 1208——水题系列——————【dp】

水题系列 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描述     给你一个有向图,每条边都有一定的权值,现在让你从图中的任意一点出发,每次走的边的权值必须必上一次的权值大的情况下,问你最多能走几条边? 输入 首先一个n和m,分别表示点的数目和边的数目接下来m行,每行三个值x,y,val,表示x到y有路,其权值为val.(1<n,m,val<10^5,0<x,y<=n) 输出 输出最多有的边的数目 样例输入 3 3 1 2 1 2 3 1 3 1 1 6

LCT男人八题系列

楼教的男人八题名气甚大,今天做了一道感觉还是涨了不少姿势的,然而估计之后的每道题都要看题解吧,姑且先记录一下.以后再做再更 1737 Connected Graph 1100 [email protected] 1738 An old Stone Game 407 [email protected] 1739 Tony's Tour 671 [email protected] 1740 A New Stone Game 2240 [email protected] 1741 Tree 1728

天题系列: LRU Cache

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.set(