In mathematics you don't understand things, you just get used to them.

分类 笔记 下的文章

Description

大家应该都读过题。

Solution

赛后变摩托😃。

我们对每一个操作 $3$ 连边建图,然后可以知道只是一个 $\texttt{DAG}$。

考虑操作 $2$,我们只需要最后计算即可。

现在来看加法操作。我们设我们当前的操作为 $a_{p}\leftarrow a_{p}+v$,后面一共有 $k$ 个乘法操作,我们设为分别让序列整体乘上 $b_{1,2,\cdots,k}$。那么 $a_{p}\leftarrow a_{p}+v$ 一共会对最后的 $a_{p}$ 产生 $v\times\prod_{i=1}^{k}b_{i}$ 的贡献。

于是我们可以把操作离线下来,倒着来处理。

那么最后来考虑操作 $3$,我们来看操作 $3$ 连出的图。

对于图上的每一个节点我们维护一个值,表示执行后会带来的系数。

那么和操作 $1$ 类似的,我们对整张图进行拓扑,然后倒着处理,处理出每个节点的操作的加法计算了多少次,然后传播(指所有相邻节点)到加法操作的节点。

最后处理原序列即可。

(代码从 $\texttt{Vim}$ 里面复制出来可能缩进有问题)

#include <queue>
#include <cstdio>
#define mod ( 998244353 )

using namespace std;
typedef long long LL;

const int MAXN = 1e6 + 5;

template<typename _T>
void read( _T &x ){
    x = 0; char c = getchar(); _T f = 1;
    while( c < '0' || c > '9' ){ if( c == '-' )    f = -1; c = getchar(); }
    while( c >= '0' && c <= '9' ){ x = ( x << 3 ) + ( x << 1 ) + ( c & 15 ); c = getchar(); }
    x *= f;
}

template<typename _T>
void write( _T x ){
    if( x < 0 ){ putchar( '-' ); x = -x; }
    if( x > 9 )    write( x / 10 );
    putchar( x % 10 + '0' );
}

struct starS{
    int to, nxt;
    starS( int T = 0, int N = 0 ){ to = T; nxt = N; }
} as[MAXN * 2];

struct operateS{
    int Tp, pos;
    LL add, mul, sum;
    operateS( int T = 0, int P = 0, LL A = 0, LL M = 0, LL S = 0 ){ Tp = T; pos = P; add = A; mul = M; sum = S; }
} opS[MAXN];

int N, M, Q;
int totE, totT;
int A[MAXN], topS[MAXN], degS[MAXN], firS[MAXN], queS[MAXN];

void pushEdge( int u, int v ){ as[++ totE] = starS( v, firS[u] ); firS[u] = totE; }

void TopSort( ){
    queue<int> align;
    for( int i = 1; i <= M; ++ i ){
        if( ! degS[i] )    align.push( i );
    }
    while( ! align.empty( ) ){
        int u = align.front( ); align.pop( );
        topS[++ totT] = u;
        for( int i = firS[u]; i; i = as[i].nxt ){
            int v = as[i].to; degS[v] --;
            if( ! degS[v] ) align.push( v );
        }
    }
}

int main( ){
    read( N );
    for( int i = 1; i <= N; ++ i )  read( A[i] );
    read( M );
    for( int i = 1; i <= M; ++ i ){
        read( opS[i].Tp );
        if( opS[i].Tp == 1 ){
            read( opS[i].pos ); read( opS[i].add );
            opS[i].mul = 1;
        }
        else if( opS[i].Tp == 2 ) {
            read( opS[i].mul );
            opS[i].add = opS[i].mul;
        }
        else{
            read( opS[i].pos );
            opS[i].mul = 1;
            for( int j = 1, to; j <= opS[i].pos; ++ j ){ read( to ); pushEdge( i, to ); degS[to] ++; }
        }
    }
    TopSort( );
    for( int i = M; i; -- i ){
        int u = topS[i];
        for( int j = firS[u]; j; j = as[j].nxt ){
            int v = as[j].to;
            opS[u].mul = ( LL )opS[u].mul * opS[v].mul % mod;
        }
    }
    read( Q ); int now = 1;
    for( int i = 1; i <= Q; ++ i )  read( queS[i] );
    for( int i = Q; i; -- i ){ opS[queS[i]].sum = ( ( LL )opS[queS[i]].sum + now ) % mod; now = ( LL )now * opS[queS[i]].mul % mod; }
    for( int i = 1; i <= M; ++ i ){
        int u = topS[i], now = 1;
        for( int j = firS[u]; j; j = as[j].nxt ){
            int v = as[j].to;
            opS[v].sum = ( ( LL )opS[u].sum * now % mod + opS[v].sum ) % mod;
            now = ( LL )now * opS[v].mul % mod;
        }
    }
    for( int i = 1; i <= N; ++ i )  A[i] = ( LL )A[i] * now % mod;
        for( int i = 1; i <= M; ++ i ){
        if( opS[i].Tp == 1 )    A[opS[i].pos] = ( A[opS[i].pos] + ( LL )opS[i].add * opS[i].sum % mod ) % mod;
    }
    for( int i = 1; i <= N; ++ i )  write( A[i] ), putchar( ' ' );
    return 0;
}

Description

Link.

$$ \sum\prod_{i=1}^{m}F_{a_{i}},(m>0,a_{1},\cdots a_{m}>0,\sum a_{i}=n) $$

Solution

这是一篇不用 $\mathbf{OGF}$ 的题解。

设 $f_{i}$ 为 $i$ 的 $\operatorname{lqp}$ 拆分值。

然后有显然的过不了递推式:

$$ f_{n}=\begin{cases} 1,n=0 \\ \displaystyle \sum_{i=0}^{n-1}F_{n-i}\times f_{i},n\neq0 \end{cases} $$

然后传统艺能错位相减操作一下:

$$ \begin{aligned} f_{n}&=\sum_{i=0}^{n-1}F_{n-i}\times f_{i} \\ f_{n-1}&=\sum_{i=0}^{n-2}F_{n-i-1}\times f_{i} \\ f_{n-2}&=\sum_{i=0}^{n-3}F_{n-i-2}\times f_{i} \end{aligned} \Longrightarrow \begin{aligned} f_{n}-f_{n-1}-f_{n-2}&=\sum_{i=0}^{n-1}F_{n-i}\times f_{i}-\sum_{i=0}^{n-2}F_{n-i-1}\times f_{i}-\sum_{i=0}^{n-3}F_{n-i-2}\times f_{i} \\ f_{n}-f_{n-1}-f_{n-2}&=(F_{2}-F_{1})\times f_{n-2}+F_{1}\times f_{n-1} \end{aligned} \\ \downarrow \\ f_{n}=2f_{n-1}+f_{n-2} $$

递推公式有了,然后矩阵快速幂:

$$ \begin{bmatrix} f_{n} \\ f_{n-1} \end{bmatrix} =\begin{bmatrix} 2f_{n-1}+f_{n-2} \\ f_{n-1} \end{bmatrix} =\begin{bmatrix} f_{n-1} \\ f_{n-2} \end{bmatrix} \times\begin{bmatrix} 2 & 1 \\ 1 & 0 \end{bmatrix} $$

这样就可以做了(吗?):

(code?)

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#define mod ( 1000000007 )

using namespace std;
typedef long long LL;

template<typename _T, typename _P>
_T qkpow( _T bas, _T one, _P fur ){
    _T res = one;
    while( fur != 0 ){
        if( fur % 2 == ( _P )1 )    res = bas * res;
        bas = bas * bas;
        fur /= 2;
    }
    return res;
}

template<typename _T>
_T add( _T x, _T y ){ if( y >= mod )    y %= mod; x += y; if( x >= mod )    x -= mod; return x; }

struct bigInt : vector<int>{
    bigInt &check( ){
        while( ! empty( ) && ! back( ) ) pop_back( );
        if( empty( ) )    return *this;
        for( unsigned i = 1; i < size( ); ++ i ){ ( *this )[i] += ( *this )[i - 1] / 10; ( *this )[i - 1] %= 10; }
        while( back( ) >= 10 ){ push_back( back( ) / 10 ); ( *this )[size( ) - 2] %= 10; }
        return *this;
    }
    bigInt( int tpN = 0 ){ push_back( tpN ); check( ); }
};
istream &operator >> ( istream &is, bigInt &tpN ){
    string s;
    is >> s; tpN.clear( );
    for( int i = s.size( ) - 1; i >= 0; --i ) tpN.push_back( s[i] - '0' );
    return is;
}
ostream &operator << ( ostream &os, const bigInt &tpN ){
    if( tpN.empty( ) )    os << 0;
    for( int i = tpN.size( ) - 1; i >= 0; --i )    os << tpN[i];
    return os;
}
bool operator != ( const bigInt &one, const bigInt &another ){
    if( one.size( ) != another.size( ) )    return 1;
    for( int i = one.size( ) - 1; i >= 0; --i ){
        if( one[i] != another[i] )    return 1;
    }
    return 0;
}
bool operator == ( const bigInt &one, const bigInt &another ){
    return ! ( one != another );
}
bool operator < ( const bigInt &one, const bigInt &another ){
    if( one.size( ) != another.size( ) )    return one.size( ) < another.size( );
    for( int i = one.size( ) - 1; i >= 0; --i ){
        if( one[i] != another[i] )    return one[i] < another[i];
    }
    return 0;
}
bool operator > ( const bigInt &one, const bigInt &another ){ return another < one; }
bool operator <= ( const bigInt &one, const bigInt &another ){ return ! (one > another ); }
bool operator >= ( const bigInt &one, const bigInt &another ){ return ! (one < another ); }
bigInt &operator += ( bigInt &one, const bigInt &another ){
    if( one.size( ) < another.size( ) )    one.resize(another.size( ) );
    for( unsigned i = 0; i != another.size( ); ++ i ) one[i] += another[i];
    return one.check( );
}
bigInt operator + ( bigInt one, const bigInt &another ){ return one += another; }
bigInt &operator -= ( bigInt &one, bigInt another ){
    if( one < another )    swap( one, another );
    for( unsigned i = 0; i != another.size( ); one[i] -= another[i], ++ i ){
        if( one[i] < another[i] ){
            unsigned j = i + 1;
            while( ! one[j] ) ++ j;
            while( j > i ){ -- one[j]; one[--j] += 10; }
        }
    }
    return one.check( );
}
bigInt operator - ( bigInt one, const bigInt &another ){ return one -= another; }
bigInt operator * ( const bigInt &one, const bigInt &another ){
    bigInt tpN;
    tpN.assign( one.size( ) + another.size( ) - 1, 0 );
    for( unsigned i = 0; i != one.size( ); ++ i ){
        for( unsigned j = 0; j != another.size( ); ++ j ) tpN[i + j] += one[i] * another[j];
    }
    return tpN.check( );
}
bigInt &operator *= ( bigInt &one, const bigInt &another ){ return one = one * another; }
bigInt divMod( bigInt &one, const bigInt &another ){
    bigInt ans;
    for( int t = one.size( ) - another.size( ); one >= another; -- t ){
        bigInt tpS;
        tpS.assign( t + 1, 0 );
        tpS.back( ) = 1;
        bigInt tpM = another * tpS;
        while( one >= tpM ){ one -= tpM; ans += tpS; }
    }
    return ans;
}
bigInt operator / ( bigInt one, const bigInt &another ){ return divMod(one, another ); }
bigInt &operator /= ( bigInt &one, const bigInt &another ){ return one = one / another; }
bigInt &operator %= ( bigInt &one, const bigInt &another ){ divMod( one, another ); return one; }
bigInt operator % ( bigInt one, const bigInt &another ){ return one %= another; }

struct matrixS{
    int mat[2][2];
    matrixS( int x = 0 ){ memset( mat, x, sizeof( mat ) ); }
    matrixS operator * ( const matrixS &another ) const{
        matrixS res;
        for( int i = 0; i < 2; ++ i ){
            for( int j = 0; j < 2; ++ j ){
                for( int k = 0; k < 2; ++ k )    res.mat[i][j] = add( ( LL )res.mat[i][j], ( LL )mat[i][k] * another.mat[k][j] );
            }
        }
        return res;
    }
} unit, erng;

bigInt N;

void progressBaseInformation( ){
    int unitS[2][2] = { { 1, 0 }, { 0, 1 } };
    memcpy( unit.mat, unitS, sizeof( unitS ) );
    int erngS[2][2] = { { 2, 1 }, { 1, 0 } };
    memcpy( erng.mat, erngS, sizeof( erngS ) );
}

signed main( ){
    ios::sync_with_stdio( 0 ); cin.tie( 0 ); cout.tie( 0 );
    progressBaseInformation( );
    cin >> N; cout << qkpow( erng, unit, N ).mat[1][0] << '\n';
    return 0;
}

不,凉心出题人友好地卡了高精的常数,于是你打开题解,发现 $f_{n}=f_{n\bmod (10^{9}+6)}$,于是你又行了。

$\mathcal{Code}$

#include <cstdio>
#include <cstring>
#include <queue>
#define mod ( 1000000007 )

using namespace std;
typedef long long LL;

template<typename _T>
void read( _T &x ){
    x = 0; char c = getchar( ); _T f = 1;
    while( c < '0' || c > '9' ){ if( c == '-' )    f = -1; c = getchar( ); }
    while( c >= '0' && c <= '9' ){ x = ( ( x << 3 ) + ( x << 1 ) + ( c & 15 ) ) % ( mod - 1 ); c = getchar( ); }
    x *= f;
}

template<typename _T>
void write( _T x ){
    if( x < 0 ){ putchar( '-' ); x = -x; }
    if( x > 9 )    write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T, typename _P>
_T qkpow( _T bas, _T one, _P fur ){
    _T res = one;
    while( fur != 0 ){
        if( fur % 2 == ( _P )1 )    res = bas * res;
        bas = bas * bas;
        fur /= 2;
    }
    return res;
}

template<typename _T>
_T add( _T x, _T y ){ if( y >= mod )    y %= mod; x += y; if( x >= mod )    x -= mod; return x; }

struct matrixS{
    int mat[2][2];
    matrixS( int x = 0 ){ memset( mat, x, sizeof( mat ) ); }
    matrixS operator * ( const matrixS &another ) const{
        matrixS res;
        for( int i = 0; i < 2; ++ i ){
            for( int j = 0; j < 2; ++ j ){
                for( int k = 0; k < 2; ++ k )    res.mat[i][j] = add( ( LL )res.mat[i][j], ( LL )mat[i][k] * another.mat[k][j] );
            }
        }
        return res;
    }
} unit, erng;

LL N;

void progressBaseInformation( ){
    int unitS[2][2] = { { 1, 0 }, { 0, 1 } };
    memcpy( unit.mat, unitS, sizeof( unitS ) );
    int erngS[2][2] = { { 2, 1 }, { 1, 0 } };
    memcpy( erng.mat, erngS, sizeof( erngS ) );
}

signed main( ){
    progressBaseInformation( );
    read( N ); write( qkpow( erng, unit, N ).mat[1][0] ), putchar( '\n' );
    return 0;
}

$\mathbf{OGF}$ 的定义

对于一个序列 $a_{1},a_{2},\cdots$,我们称:

$$ G(x)=\sum_{i=0}^{\infty}a_{i}x^{i} $$

为序列 $a$ 的 $\mathbf{OGF}$ 即普通生成函数 ($\texttt{Ordinary Generating Function}$)。

同时因为我们不关心 $x$ 的取值,因此 $\sum_{i=1}^{+\infty}a_{i}x^{i}$ 又称作以 $x$ 为自由元的形式幂级数。 -- 摘自 自为风月马前卒

一个例子

举个例子,序列:

$$ \left(^{n}_{0}\right),\left(^{n}_{1}\right),\cdots,\left(^{n}_{n}\right) $$

的 $\mathbf{OGF}$ 为(二项式定理):

$$ G(x)=(1+x)^{n} $$

由等比数列求和公式,有一个常用的等式:

$$ \sum_{i=0}^{\infty}x^{i}=\frac{1-x^{\infty}}{1-x} $$

因为指数为 $\infty$,所以 $x^{\infty}$ 趋近于 $0$,箭头方向随便打,因为我们并不关心 $x$ 的取值。

$$ \sum_{i=0}^{\infty}x^{i}=\frac{1}{1-x} $$

这个等式还有一个重要的运用,我们把 $x$ 替换成 $kx$ 即可得:

$$ \sum_{i=0}^{\infty}(kx)^{i}=\frac{1}{1-kx} $$

后文的用 $\mathbf{OGF}$ 求序列的通项公式里面这个东西很有用的。

$\texttt{Fibonacci}$ 序列的生成函数求法

定义一个序列

$$ F_{i}=\begin{cases} 1,i\in[0,1] \\ \displaystyle F_{i-1}+F_{i-2},i\in[2,\infty) \end{cases} $$

则我们称 $F$ 为 $\texttt{Fibonacci}$ 序列。

接下来我们来推导其生成函数:

$$ \begin{aligned} G(x)&=\sum_{i=0}^{\infty}F_{i}x^{i} \\ G(x)&=1+x+2x^{2}+3x^{3}+\cdots \\ xG(x)&=x+x^{2}+2x^{3}+3x^{4}+\cdots \\ x^{2}G(x)&=x^{2}+x^{3}+2x^{4}+3x^{5}+\cdots \end{aligned} $$

这里运用初中数学中经常用的到错位相减这一小技巧,可得

$$ G(x)-xG(x)-x^{2}G(x)=1 $$

即可得

$$ G(x)=\frac{1}{1-x-x^{2}} $$

至此,我们已经求出了 $\texttt{Fibonacci}$ 序列的 $\mathbf{OGF}$ 了。

利用生成函数求数列通项

以前文提到的 $\texttt{Fibonacci}$ 为例。

首先我们知道其 $\mathbf{OGF}$ 为:

$$ G(x)=\frac{1}{1-x-x^{2}} $$

待定系数一下分母我们就可以得到:

$$ G(x)=\frac{1}{(1-\frac{1+\sqrt{5}}{2}x)(1-\frac{1-\sqrt{5}}{2}x)} $$

~后面的还没推出来,咕了~

Description

Link.

区间众数出现次数强制在线。

Solution

三个 YLST 中比较清新的一个分块。

比较重点的地方在于询问散块的处理。

先离散化一下序列。

我们首先预处理出来一个 vector 数组 fur[i]fur[i] 里面依次存的是所有 isa[i](即这个序列,详见代码)的出现位置,再预处理一个 pos[i] 表示在当前第 $i$ 位时 fur[i] 的大小也就是一共出现了多少个 isa[i]。由于 vector 的下标是从 $0$ 开始的,所以所有的 pos[i] 都需要减个一。

然后询问处理整块的时候,我们先假设当前询问的区间是 [opl,opr],然后把当前询问的答案 res 先置为 App[bel[opl] + 1][bel[opr] - 1]

然后来考虑散块,在处理出的 vector 数组中判断即可。

设散块处理到数 isa[i],那么如果存在 pos[i] + res <= fur[isa[i]].size() - 1fur[isa[i]][pos[i] + res] <= opr,那么则说明这个数出现了至少 res + 1 次,将 res 加一即可。

由于 res 最多加不超过 $\Theta(2\sqrt{n})$ 次,所以复杂度是对的。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 5e5 + 5, MAXM = 720 + 5;

char buf[1 << 21], *p1 = buf, *p2 = buf;
#define getchar( ) ( p1 == p2 && ( p2 = ( p1 = buf ) + fread( buf, 1, 1 << 21, stdin ), p1 == p2 ) ? EOF : *p1 ++ )

template<typename _T>
void read( _T &x ){
    x = 0; char c = getchar( ); _T f = 1;
    while( c < '0' || c > '9' ){ if( c == '-' )    f = -1; c = getchar( ); }
    while( c >= '0' && c <= '9' ){ x = ( x << 3 ) + ( x << 1 ) + ( c & 15 ); c = getchar( ); }
    x *= f;
}

template<typename _T>
void write( _T x ){
    if( x < 0 ){ putchar( '-' ); x = -x; }
    if( x > 9 ){ write( x / 10 ); }
    putchar( x % 10 + '0' );
}

template<typename _T>
void swapp( _T &one, _T &another ){ int temp = one; one = another; another = temp; }

template<typename _T>
_T MIN( _T one, _T another ){ return one > another ? another : one; }

template<typename _T>
_T MAX( _T one, _T another ){ return one > another ? one : another; }

int N, M;
int cube, each, kase, isa[MAXN], cnt[MAXN], pos[MAXN], vis[MAXN], bel[MAXN];
int lps[MAXM], rps[MAXM], app[MAXM], App[MAXM][MAXM];
vector<int> disc, fur[MAXN];

int getID( int x ){ return lower_bound( disc.begin( ), disc.end( ), x ) - disc.begin( ) + 1; }

void build( ){
    for( int i = 1; i <= cube; ++ i ){
        kase ++;
        for( int j = lps[i]; j <= rps[i]; ++ j ){
            if( vis[isa[j]] != kase )    cnt[isa[j]] = 0;
            cnt[isa[j]] ++; app[i] = MAX( app[i], cnt[isa[j]] );
            vis[isa[j]] = kase;
        }
    }
    memset( cnt, 0, sizeof( cnt ) );
    for( int i = 1; i <= cube; ++ i ){
        kase ++;
        for( int j = i; j <= cube; ++ j ){
            App[i][j] = App[i][j - 1];
            for( int k = lps[j]; k <= rps[j]; ++ k ){
                if( vis[isa[k]] != kase )    cnt[isa[k]] = 0;
                cnt[isa[k]] ++; App[i][j] = MAX( App[i][j], cnt[isa[k]] );
                vis[isa[k]] = kase;
            }
        }
    }
    memset( cnt, 0, sizeof( cnt ) );
}

int query( int opl, int opr ){
    if( bel[opl] == bel[opr] ){
        int res = 0; kase ++;
        for( int i = opl; i <= opr; ++ i ){
            if( vis[isa[i]] != kase )    cnt[isa[i]] = 0;
            cnt[isa[i]] ++; res = MAX( res, cnt[isa[i]] );
            vis[isa[i]] = kase;
        }
        return res;
    }
    int res = 0;
//    res = App[bel[opl] + 1][bel[opr] - 1];
    for( int i = bel[opl] + 1; i < bel[opr]; ++ i )    res += app[i];
//    for( int i = bel[opl] + 1; i < bel[opr]; ++ i )    res += App[i][i];
    for( int i = opl; i <= rps[bel[opl]]; ++ i ){
        int lim = fur[isa[i]].size( ) - 1;
        while( pos[i] + res <= lim && fur[isa[i]][pos[i] + res] <= opr )    res ++;
    }
    for( int i = lps[bel[opr]]; i <= opr; ++ i ){
        while( pos[i] - res >= 0 && fur[isa[i]][pos[i] - res] >= opl )    res ++;
    }
    return res;
}

signed main( ){
    read( N ); read( M ); each = 720; cube = ( N - 1 ) / each + 1;
    for( int i = 1; i <= N; ++ i ){ read( isa[i] ); disc.push_back( isa[i] ); }
    sort( disc.begin( ), disc.end( ) );
    disc.erase( unique( disc.begin( ), disc.end( ) ), disc.end( ) );
    for( int i = 1; i <= N; ++ i ){
        isa[i] = getID( isa[i] );
        fur[isa[i]].push_back( i );
        pos[i] = fur[isa[i]].size( ) - 1;
    }
    for( int i = 1; i <= cube; ++ i ){
        lps[i] = rps[i - 1] + 1; rps[i] = rps[i - 1] + each;
        if( i == cube )    rps[i] = N;
        for( int j = lps[i]; j <= rps[i]; ++ j )    bel[j] = i;
    }
    build( );
    int Ans = 0, opl, opr;
    while( M -- > 0 ){
        read( opl ); read( opr ); opl ^= Ans; opr ^= Ans;
        Ans = 0; if( opl > opr )    swapp( opl, opr );
        write( Ans = query( opl, opr ) ); putchar( '\n' );
    }
    return 0;
}

Description

Link.

见 Link。

Solution

前三个操作就是小清新人渣的本愿。

这里简单讲解一下。

记录两个 bitset clainv

我们考虑莫队。

cla[x]==1 表示 $x$ 这个数出现过。

inv[x]==1 表示 $100000-x$ 这个数出现过。

这两个 bitset 的维护很简单,就是在莫队的加减贡献操作里改就行了。

对于第一个减法的判断,我们的答案就是 ((cla<<x)&cla) 是否为 0。

如果为 0 的话表示应该输出有解。

正确性很好得到。

比如我们的询问是是否存在 $a,b$ 使得 $a-b=x$。

那么我们只需要存在 $a$ 以及 $a-x$ 即可。

第二个加法的判断也差不多,看作是加一个负数即可,判断是 ((cla<<(100000-x)&inv))

第三个乘法的判断直接暴力枚举因子 $i$,判断 $i,\frac{x}{i}$ 是否同时存在即可。($i\mid x$)。

由于值域和 $n,m$ 同阶,所以我们的复杂度是对的。

对于第四个操作我们直接从乘法贺过来。

枚举一个 $i$,从 1 开始,终止条件为 $i\times x\le100000$。

其中 $x$ 为当前询问给出的商。

然后直接判断是否同时存在 $i$ 和 $i\times x$ 即可。

在 $x\ge\sqrt{n}$ 的时候我们的复杂度是对的。

那么 $x<\sqrt{n}$ 的时候我们就换一种方法吧。

我们枚举一个 $x\in[1,\sqrt{100000}]$。

然后维护两个数组 premxp

在 $x$ 的枚举里面我们再枚举一个 $i\in[1,n]$。

然后 pre[i] 表示 $a_{i}$ 的上一次出现位置。

mxp[i] 扫描到 $i$ 的时候出现了满足 $a\div b=x$ 的最右位置。

维护的具体方法看注释吧。

这道题就完了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <bitset>
#include <queue>

using namespace std;

const int Maxn = 1e5 + 5, Maxs = 400 + 5, Maxv = 310;
int n, m, each, cube, isa[Maxn], cnt[Maxn], ans[Maxn], pre[Maxn], mxp[Maxn], bel[Maxn], lps[Maxs], rps[Maxs];
struct Query
{
    int t, l, r, x, id;
    Query() {}
    Query(int t1, int t2, int t3, int t4, int t5)
    {
        t = t1;
        l = t2;
        r = t3;
        x = t4;
        id = t5;
    }
};
struct Special
{
    int l, r, id;
    Special() {}
    Special(int t1, int t2, int t3)
    {
        l = t1;
        r = t2;
        id = t3;
    }
};
vector < Query > Mq; // Mo's Algorithm | Query
vector < Special > Vq[Maxn]; // Violence | Query
bitset < Maxn > cla, inv; // classic | inverse

bool cmp(const Query &one, const Query &ano)
{
    if (bel[one.l] != bel[ano.l])   return one.l < ano.l;
    else if (bel[one.l] & 1)    return one.r < ano.r;
    else    return one.r > ano.r;
}

void Plus_Cont(int x)
{
    x = isa[x];
    if (cnt[x] == 0)
    {
        cla[x] = 1;
        inv[100000 - x] = 1;
    }
    ++cnt[x];
}

void Mins_Cont(int x)
{
    x = isa[x];
    --cnt[x];
    if (cnt[x] == 0)
    {
        cla[x] = 0;
        inv[100000 - x] = 0;
    }
}

void Pare_v1()
{
    int l = 1, r = 0;
    for (auto it : Mq)
    {
        while (l > it.l)    Plus_Cont(--l);
        while (l < it.l)    Mins_Cont(l++);
        while (r > it.r)    Mins_Cont(r--);
        while (r < it.r)    Plus_Cont(++r);
        if (it.t == 1)  ans[it.id] = ((cla << it.x) & cla).any();
        else if (it.t == 2)  ans[it.id] = ((cla << (100000 - it.x)) & inv).any();
        else if (it.t == 3)
        {
            bool flag = 0;
            for (int i = 1; i * i <= it.x; ++i)
            {
                if (it.x % i == 0 && cla.test(i) && cla.test(it.x / i))
                {
                    ans[it.id] = 1;
                    flag = 1;
                    break;
                }
            }
            if (flag == 0)  ans[it.id] = 0;
        }
        else
        {
            bool flag = 0;
            for (int i = 1; i * it.x <= 100000; ++i)
            {
                if (cla.test(i) && cla.test(i * it.x))
                {
                    ans[it.id] = 1;
                    flag = 1;
                    break;
                }
            }
            if (flag == 0)    ans[it.id] = 0;
        }
    }
}

void Pare_v2()
{
    for (int x = 1; x <= Maxv; ++x)
    {
        if (Vq[x].empty())    continue;
        int now = 0;
        for (int i = 1; i <= n; ++i)
        {
            int y = isa[i];
            pre[y] = i;
            if (x * y <= 100000)    now = max(now, pre[x * y]);
            if (y % x == 0)     now = max(now, pre[y / x]);
            mxp[i] = now;
        }
        for (auto it : Vq[x])
        {
            if (it.l <= mxp[it.r])    ans[it.id] = 1;
            else    ans[it.id] = 0; 
        }
        memset(pre, 0, sizeof pre);
        memset(mxp, 0, sizeof mxp);
    }
}

char ANS[2][10] = { "yumi", "yuno" };
signed main()
{
    scanf("%d %d", &n, &m);
    each = 320;
    cube = (n - 1) / each + 1;
    for (int i = 1; i <= n; ++i)    scanf("%d", &isa[i]);
    for (int i = 1; i <= cube; ++i)
    {
        lps[i] = rps[i - 1] + 1;
        rps[i] = rps[i - 1] + each;
        if (i == cube)     rps[i] = n;
        for (int j = lps[i]; j <= rps[i]; ++j)  bel[j] = i;
    }
    for (int i = 1, t, l, r, x; i <= m; ++i)
    {
        scanf("%d %d %d %d", &t, &l, &r, &x);
        if (t == 4 && x <= Maxv)    Vq[x].emplace_back(Special(l, r, i));
        else    Mq.emplace_back(Query(t, l, r, x, i));
    }
    sort(Mq.begin(), Mq.end(), cmp);
    Pare_v1(), Pare_v2();
    for (int i = 1; i <= m; ++i)    puts(ANS[ans[i]]);
    return 0;
}