bzoj4373: 算术天才⑨与等差数列

题面在这里

题意:

给一个序列
每次会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。
当然还会不断修改其中的某一项。

做法:

吐槽一下= =辣鸡码农。。。

一个区间要满足能成公差为k的等差数列,对于k=0和l=r的特判,
其余需要满足3个条件。
1.区间max-min = (r-l)*k
2.区间任意相邻两数差的绝对值的gcd能被k整除
3.区间没有相同的数

然后前两个都可以线段树直接维护。
第三个条件,我们可以对于每个数维护一个last表示这个值上一次出现的位子。
那么满足第三个条件必须没有满足区间最大的last>l。
然后这个东西不是很好修改是不是啊。。。
所以我们要对于每一个值都开一个set记录它出现过的位子。
更改的时候找它在set里的前驱后继瞎搞搞就好了。
具体还是见代码吧。(发现我好像已经练成压行大法)

代码:

/*************************************************************
    Problem: bzoj 4373 算术天才⑨与等差数列
    User: fengyuan
    Language: C++
    Result: Accepted
    Time: 6148 ms
    Memory: 86420 kb
    Submit_Time: 2018-01-03 19:48:32
*************************************************************/

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<set>
#include<map>
#define mid (l+r>>1)
#define lc o<<1
#define rc o<<1|1
using namespace std;
typedef long long LL;

inline LL read()
{
    char ch = getchar(); LL x = 0; int op = 1;
    for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
    for(; isdigit(ch); ch = getchar()) x = x*10 + ch-'0';
    return x*op;
}

const int N = 300010, M = 2000010;
int n, m, Mi, Ma, G, flag, total;
int a[N], b[N], c[N], d[N], mi[M], mx[M], p[M], g[M];//mi,mx是区间最小、最大值,p存区间pre的最大值,g存差值的gcd
set<int> T[N<<1];
map<int, int> MP;

inline int get(int x)
{
    if(MP.count(x)) return MP[x];
    MP[x] = ++ total;
    T[total].insert(0); T[total].insert(n+1);
    return total;
}
inline int gcd(int a, int b) { return (!b) ? a : gcd(b, a%b); }
inline void pushup(int o)
{
    mi[o] = min(mi[lc], mi[rc]); mx[o] = max(mx[lc], mx[rc]);
    p[o] = max(p[lc], p[rc]); g[o] = gcd(g[lc], g[rc]);
}
inline void build(int o, int l, int r)
{
    if(l == r) { mi[o] = mx[o] = a[l]; p[o] = c[l]; g[o] = d[l]; return; }
    build(lc, l, mid); build(rc, mid+1, r); pushup(o);
}
inline void ask(int o, int l, int r, int x, int y)
{
    if(l == x && r == y) { Mi = min(Mi, mi[o]); Ma = max(Ma, mx[o]); if(p[o] >= x) flag = 1; return; }
    if(y <= mid) ask(lc, l, mid, x, y); else if(x > mid) ask(rc, mid+1, r, x, y);
    else ask(lc, l, mid, x, mid), ask(rc, mid+1, r, mid+1, y);
}
inline void askd(int o, int l, int r, int x, int y)
{
    if(l == x && r == y) { G = gcd(G, g[o]); return; }
    if(y <= mid) askd(lc, l, mid, x, y); else if(x > mid) askd(rc, mid+1, r, x, y);
    else askd(lc, l, mid, x, mid), askd(rc, mid+1, r, mid+1, y);
}
inline bool check(int x, int y, int z)
{
    if(x == y) return 1;
    Mi = 1e9; Ma = G = flag = 0; ask(1, 1, n, x, y);
    if(!z) return Mi == Ma;
    if(flag) return 0;
    if((Ma-Mi) != (y-x)*z) return 0;
    askd(1, 1, n, x, y-1); return G%z == 0;
}
inline void change(int o, int l, int r, int x, int y, int pos)
{
    if(l == r) { mi[o] = mx[o] = y; p[o] = pos; return; }
    if(x <= mid) change(lc, l, mid, x, y, pos); else change(rc, mid+1, r, x, y, pos);
    pushup(o);
}
inline void changep(int o, int l, int r, int x, int y)
{
    if(l == r) { p[o] = y; return; }
    if(x <= mid) changep(lc, l, mid, x, y); else changep(rc, mid+1, r, x, y);
    pushup(o);
}
inline void changed(int o, int l, int r, int x, int y)
{
    if(l == r) { g[o] = y; return; }
    if(x <= mid) changed(lc, l, mid, x, y); else changed(rc, mid+1, r, x, y);
    pushup(o);
}
int main()
{
    n = read(), m = read();
    for(int i = 1; i <= n; i ++) {
        a[i] = read();
        T[b[i] = get(a[i])].insert(i);//b[i]存a[i]的标号;T[i]是i这个标号的set,存i这个值的编号
        set<int>::iterator tmp = T[b[i]].find(i);
        c[i] = *(-- tmp);//c[i]表示i这个位子的值前一次出现的位子
    }
    for(int i = 1; i < n; i ++) d[i] = abs(a[i]-a[i+1]);//d[i]存a[i]和a[i+1]的绝对值差
    build(1, 1, n); int sum = 0;
    while(m --) {
        int opt = read(), x = read()^sum, y = read()^sum, z;
        if(opt == 1) {
            set<int>::iterator pre, nxt;
            pre = nxt = T[b[x]].find(x); pre --; nxt ++;
            if(*nxt < n) changep(1, 1, n, *nxt, *pre);
            T[b[x]].erase(x);
            b[x] = get(y);
            T[b[x]].insert(x);
            pre = nxt = T[b[x]].find(x); pre --, nxt ++;
            if(*nxt < n) changep(1, 1, n, *nxt, x);
            change(1, 1, n, x, a[x] = y, *pre);
            if(x > 1) changed(1, 1, n, x-1, abs(a[x-1]-y));
            if(x < n) changed(1, 1, n, x, abs(a[x+1]-y));
        } else {
            z = read()^sum;
            if(check(x, y, z)) puts("Yes"), sum ++; else puts("No");
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值