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

标签 data structures 下的文章

Prob. 1

Desc. & Link.

暴力为 $\Theta(NK)$。

正解(也许):

把每一个全为正整数的子段找出来。

然后判断一下中间连接的情况即可。

但是这样决策情况太多了。

我们需要考虑贪心。

把所有整数段的个数记为 $totP$,每个子段的区间记为 $[posL_{i},posR_{i}]$,区间和记为 $sumP_{i}$

把其他的负数段个数记为 $totN$,区间和记为 $sumN_{i}$。

当 $totP\le k$ 答案显然。

我们需要考虑的是 $totP>k$ 的情况。

我们把整数段、负数段缩成点。

然后问题还是最多选 $k$ 段的最大子段和。

不过我们的序列有个性质:相邻数的正负性不同。(gu)

好了放弃以上想法。

模拟 $k$ 轮找全局最大子段和,找到一次把子段乘上 $-1$。

#include <cstdio>

typedef long long LL;

const int MAXN = 1e5 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -1 : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<typename _T>
void wint ( _T x ) {
    if ( x < 0 )    putchar ( '-' ), x = ~ x + 1;
    if ( x > 9 )    wint ( x / 10 );
    putchar ( x % 10 ^ '0' );
}

template<typename _T> _T MAX ( const _T x, const _T y ) { return x < y ? y : x; }
template<typename _T> void swapp ( _T &x, _T &y ) { _T w = x; x = y; y = w; }

struct nodeS {
    LL val, dat, p, s;
    int l, r, pl, pr, sl, sr;
    nodeS ( LL V = 0, LL D = 0, LL P = 0, LL S = 0,
            int L = 0, int R = 0, int Pl = 0, int Pr = 0, int Sl = 0, int Sr = 0 ) {
                val = V, dat = D, p = P, s = S, l = L, r = R, pl = Pl, pr = Pr, sl = Sl, sr = Sr; }
} nodes[MAXN * 4][2];

int n, k, a[MAXN];
bool tag[MAXN * 4];

nodeS Merge ( const nodeS lch, const nodeS rch ) {
    nodeS ret;
    ret.val = lch.val + rch.val;
    ret.p = MAX ( lch.p, lch.val + rch.p );
    if ( ret.p == lch.p )    ret.pl = lch.pl, ret.pr = lch.pr;
    else    ret.pl = lch.pl, ret.pr = rch.pr;
    ret.s = MAX ( rch.s, rch.val + lch.s );
    if ( ret.s == rch.s )    ret.sl = rch.sl, ret.sr = rch.sr;
    else    ret.sl = lch.sl, ret.sr = rch.sr;
    ret.dat = MAX ( lch.s + rch.p, MAX ( lch.dat, rch.dat ) );
    if ( ret.dat == lch.dat )    ret.l = lch.l, ret.r = lch.r;
    else if ( ret.dat == rch.dat )    ret.l = rch.l, ret.r = rch.r;
    else    ret.l = lch.sl, ret.r = rch.pr;
    return ret;
}

void Upt ( const int x ) {
    nodes[x][0] = Merge ( nodes[x << 1][0], nodes[x << 1 | 1][0] );
    nodes[x][1] = Merge ( nodes[x << 1][1], nodes[x << 1 | 1][1] );
}

void Spr ( const int x ) {
    if ( ! tag[x] )    return;
    swapp ( nodes[x << 1][0], nodes[x << 1][1] );
    swapp ( nodes[x << 1 | 1][0], nodes[x << 1 | 1][1] );
    tag[x << 1] ^= 1, tag[x << 1 | 1] ^= 1, tag[x] = 0;
}

void Build ( const int x, const int l, const int r ) {
    if ( l == r ) {
        nodes[x][0] = nodeS ( a[l], a[l], a[l], a[l], l, l, l, l, l, l );
        nodes[x][1] = nodeS ( -a[l], -a[l], -a[l], -a[l], l, l, l, l, l, l );
        return;
    }
    int mid = ( l + r ) >> 1;
    Build ( x << 1, l, mid );
    Build ( x << 1 | 1, mid + 1, r );
    Upt ( x );
}

void Modify ( const int x, const int l, const int r, const int segL, const int segR ) {
    if ( l > segR || r < segL )    return;
    if ( l >= segL && r <= segR ) {
        swapp ( nodes[x][0], nodes[x][1] );
        tag[x] ^= 1;
        return;
    }
    int mid = ( l + r ) >> 1;
    Spr ( x );
    Modify ( x << 1, l, mid, segL, segR );
    Modify ( x << 1 | 1, mid + 1, r, segL, segR );
    Upt ( x );
}

int main () {
    n = rint (), k = rint ();
    for ( int i = 1; i <= n; ++ i )    a[i] = rint ();
    Build ( 1, 1, n );
    LL ans = 0;
    while ( k -- > 0 ) {
        nodeS ret = nodes[1][0];
        if ( ret.dat < 0 )    break;
        Modify ( 1, 1, n, ret.l, ret.r );
        ans += ret.dat;
    }
    wint ( ans ), putchar ( '\n' );
    return 0;
}

Prob. 2

Desc. & Link.

设 $f_{i,0/1}$ 表示把 $a_{i}$ 往头/尾放可以得到的最多的上升子序列。

$$ f_{i,0}=\begin{cases}\max\{f_{j,0},f_{j,1}\}+1,a_{i}<a_{j} \\\max\{f_{j,0},f_{j,1}\},a_{i}\ge a_{j}\end{cases} \\f_{i,1}=\begin{cases}\max\{f_{j,0},f_{j,1}\},a_{i}<a_{j} \\\max\{f_{j,0},f_{j,1}\}+1,a_{i}\ge a_{j}\end{cases} $$

不行。

考虑普通的 LIS 怎么做。

$$ f_{i}=\max\{f_{j}\}+1,a_{i}>a_{j} $$

$$ a_{i}<a_{j},i<j $$

选择往前放的元素,放得越晚越靠前。

选择往后放的元素,放得越晚越靠后。

那么需要做的是把相对较大的元素往后,相对较小的元素往前。

连边,把李三花后的 $a_{i}$ 连向 $\text{trans}(i,0),\text{trans}(i,1),\text{trans}(x,0/1)=n-x+1/x+n$。

#include <cstdio>
#include <algorithm>

using namespace std;

const int MAXN = 2e5 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -1 : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<typename _T>
void wint ( _T x ) {
    if ( x < 0 )    putchar ( '-' ), x = ~ x + 1;
    if ( x > 9 )    wint ( x / 10 );
    putchar ( x % 10 ^ '0' );
}

template<typename _T> _T MAX ( const _T x, const _T y ) { return x < y ? y : x; }

struct Value {
    int val, pos;
    Value ( int V = 0, int P = 0 ) { val = V, pos = P; }
    bool operator < ( const Value &another ) { return val < another.val; }
} vals[MAXN];

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

int n, cnt, len, degin[MAXN], a[MAXN], b[MAXN], buc[MAXN], sywf[MAXN];

void makeEdge ( const int u, const int v ) { as[++ cnt] = GraphSet ( v, degin[u] ), degin[u] = cnt; }
int Trans ( const int x, const int y ) { return ! y ? n - x + 1 : x + n; }

void ADD ( int p, const int x ) { for ( ; p <= ( n << 1 ); p += p & -p )    sywf[p] = MAX ( sywf[p], x ); }
int ASK ( int p ) { int res = 0; for ( ; p; p -= p & -p )    res = MAX ( res, sywf[p] ); return res; }
int CMP ( const int x, const int y ) { return x > y; }

int main () {
//    freopen ( "dequexlis.in", "r", stdin );
//    freopen ( "dequexlis.out", "w", stdout );
    n = rint ();
    for ( int i = 1; i <= n; ++ i )    a[i] = b[i] = rint ();
    sort ( b + 1, b + 1 + n );
    len = unique ( b + 1, b + 1 + n ) - b - 1;
    for ( int i = 1; i <= n; ++ i )    a[i] = lower_bound ( b + 1, b + 1 + len, a[i] ) - b;
    for ( int i = 1; i <= n; ++ i )    vals[i] = Value ( a[i], i );
    for ( int i = 1; i <= n; ++ i ) {
        makeEdge ( a[i], Trans ( i, 0 ) );
        makeEdge ( a[i], Trans ( i, 1 ) );
    }
    int BUC = 0;
    for ( int x_x = 1; x_x <= n; ++ x_x ) {
        int u = x_x;
        BUC = 0;
        for ( int i = degin[u]; i; i = as[i].nx ) {
            int v = as[i].to;
            buc[++ BUC] = v;
        }
        sort ( buc + 1, buc + 1 + BUC, CMP );
        for ( int i = 1; i <= BUC; ++ i )    ADD ( buc[i], 1 + ASK ( buc[i] - 1 ) );
    }
    wint ( ASK ( n << 1 ) ), putchar ( '\n' );
    return 0;
}

/* Jesus bless all */

Prob. 3

放弃人生打了个 50 的记搜。

#include <cstdio>
#include <map>
#define mod ( 998244853 )

using namespace std;

template<typename _T> _T MAX ( const _T x, const _T y ) { return x < y ? y : x; }

int n, m;

namespace Course {
const int MAXN = 255;
int f[MAXN][MAXN][MAXN];
bool vis[MAXN][MAXN][MAXN];

int dfs ( const int mx, const int a, const int b ) {
    if ( vis[a][b][mx] )    return f[a][b][mx];
    vis[a][b][mx] = 1;
    if ( a == n && b == m )    return f[a][b][mx] = mx;
    if ( a < n )    f[a][b][mx] = ( f[a][b][mx] + dfs ( MAX ( mx, a - b + 1 ), a + 1, b ) ) % mod;
    if ( b < m )    f[a][b][mx] = ( f[a][b][mx] + dfs ( mx, a, b + 1 ) ) % mod;
    return f[a][b][mx];
}
}

int main () {
    // freopen ( "maxpsum.in", "r", stdin );
    // freopen ( "maxpsum.out", "w", stdout );
    scanf ( "%d%d", &n, &m );
    if ( n <= 250 && m <= 250 )    printf ( "%d\n", Course :: dfs ( 0, 0, 0 ) );
    // else    printf ( "%d\n", Might :: dfs ( 0, 0, 0 ) );
    return 0;
}

Prob. 4

Desc. & Link.

LOC 2020.11.20 - Prob. 1

Desc. & Link.

$C=2^{k}\bmod(a+b+c)$

#include <cstdio>

typedef long long LL;

int A, B, C, K;

int Qkpow( int base, int indx, int mod ){
    int res = 1;
    while( indx ){
        if( indx & 1 )    res = ( LL )res * base % mod;
        base = ( LL )base * base % mod;
        indx >>= 1;
    }
    return res;
}

int main( ){
    int TC; scanf( "%d", &TC ); while( TC -- > 0 ){
        scanf( "%d%d%d%d", &A, &B, &C, &K );
        printf( "%lld\n", ( LL )C * Qkpow( 2, K, A + B + C ) % ( A + B + C ) );
    }
    return 0;
}

LOC 2020.11.20 - Prob. 2

Desc. & Link.

我先行否决 naive 的线段树。

输入暗示连边?

那就连吧。

考虑每一个连通块 $S$,如果连通块是个树,就只能选 $|S|-1$ 个点。

如果存在环,那么每个数都取得到。

那么可以的情况就是询问区间包含的不是一棵树。

#include <cstdio>

const int MAXN = 2e5 + 5;

template<typename _T> _T MIN( const _T x, const _T y ){ return x > y ? y : x; }
template<typename _T> _T MAX( const _T x, const _T y ){ return x > y ? x : y; }

struct pointS{
    int one, ano;
    pointS( int O = 0, int A = 0 ){ one = A; ano = A; }
} pntS[MAXN];

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

int N, K, Q;
int totE;
int firS[MAXN], furS[MAXN], enS[MAXN], mxV[MAXN], mnV[MAXN], vis[MAXN];

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

void DFS( const int u, int &mxVt, int &mnVt, int &edgeS, int &nodeS ){
    mxVt = MAX( mxVt, u ); mnVt = MIN( mnVt, u );
    edgeS += furS[u]; nodeS ++; vis[u] = 1;
    for( int i = firS[u]; i; i = as[i].nx ){
        int v = as[i].to;
        if( vis[v] )    continue;
        DFS( v, mxVt, mnVt, edgeS, nodeS );
    }
}

int main( ) {
    scanf( "%d%d", &N, &K );
    for( int i = 1; i <= K; ++ i ){
        scanf( "%d%d", &pntS[i].one, &pntS[i].ano );
        pushEdge( pntS[i].one, pntS[i].ano );
        pushEdge( pntS[i].ano, pntS[i].one );
    }
    for( int i = 1; i <= N; ++ i )    enS[i] = N + 1;
    for( int i = 1; i <= N; ++ i ){
        if( vis[i] )    continue;
        int mxVt = 1, mnVt = N, edgeS = 0, nodeS = 0;
        DFS( i, mxVt, mnVt, edgeS, nodeS ); edgeS >>= 1;
        if( edgeS == nodeS - 1 )    enS[mnVt] = mxVt;
    }
    for( int i = N - 1; i; -- i )    enS[i] = MIN( enS[i], enS[i + 1] );
    scanf( "%d", &Q );
    while( Q -- > 0 ){
        int queL, queR;
        scanf( "%d%d", &queL, &queR );
        if( enS[queL] > queR )    printf( "Yes\n" );
        else    printf( "No\n" );
    }
    return 0;
}

LOC 2020.11.20 - Prob. 3 / CF396C On Changing Tree

Desc. & Link.

哇哦。

喜闻乐见的 DS 题。

大约是届一下 $\texttt{lazy tag}$ 的思想。

对于一个节点 $u$,只有在路径 $(1,u)$ 上的修改才会产生影响。

这个询问有够简单,差个分即可。

修改就子树加 $x$,再加个 $k$(除根节点)。

树剖,完了。

#include <cstdio>
#define mod ( 1000000007 )

typedef long long LL;

const int MAXN = 3e5 + 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' );
}

template<typename _T> void swapp( _T &x, _T &y ){ int w = x; x = y; y = w; }

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

struct nodeS{
    int val, add;
    nodeS( int V = 0, int A = 0 ){ val = V; add = A; }
} nodes[MAXN * 4];

int N, M;
int sjc, cnt;
int firS[MAXN], posL[MAXN], posR[MAXN], top[MAXN], son[MAXN], dep[MAXN], fur[MAXN], fa[MAXN];

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

void oneSearch( const int u, const int lst ){
    fa[u] = lst; dep[u] = dep[lst] + 1; fur[u] = 1;
    for( int i = firS[u]; i; i = as[i].nx ){
        int v = as[i].to;
        oneSearch( v, u );
        fur[u] += fur[v];
        if( fur[v] > fur[son[u]] )    son[u] = v;
    }
}

void anotherSearch( const int u, const int nTp ){
    top[u] = nTp; posL[u] = ++ sjc;
    if( son[u] )    anotherSearch( son[u], nTp );
    for( int i = firS[u]; i; i = as[i].nx ){
        int v = as[i].to;
        if( v == son[u] )    continue;
        anotherSearch( v, v );
    }
    posR[u] = sjc;
}

void Spr( const int x, const int l, const int r ){
    if( ! nodes[x].add )    return;
    int mid = ( l + r ) >> 1;
    nodes[x << 1].val = ( nodes[x << 1].val + ( LL )nodes[x].add * ( mid - l + 1 ) % mod ) % mod;
    nodes[x << 1 | 1].val = ( nodes[x << 1 | 1].val + ( LL )nodes[x].add * ( r - mid ) % mod ) % mod;
    nodes[x << 1].add = ( nodes[x << 1].add + nodes[x].add ) % mod;
    nodes[x << 1 | 1].add = ( nodes[x << 1 | 1].add + nodes[x].add ) % mod;
    nodes[x].add = 0;
}

void Upt( const int x ){ nodes[x].val = ( nodes[x << 1].val + nodes[x << 1 | 1].val ) % mod; }

void Modify( const int x, const int l, const int r, const int segL, const int segR, const int segW ){
    if( l > segR || r < segL )    return;
    if( l >= segL && r <= segR ){
        nodes[x].val = ( nodes[x].val + ( LL )segW * ( r - l + 1 ) % mod ) % mod;
        nodes[x].add = ( nodes[x].add + segW ) % mod;
        return;
    }
    int mid = ( l + r ) >> 1;
    Spr( x, l, r );
    Modify( x << 1, l, mid, segL, segR, segW );
    Modify( x << 1 | 1, mid + 1, r, segL, segR, segW );
    Upt( x );
}

int Query( const int x, const int l, const int r, const int segL, const int segR ){
    if( l > segR || r < segL )    return 0;
    if( l >= segL && r <= segR )    return nodes[x].val;
    int mid = ( l + r ) >> 1; Spr( x, l, r );
    return ( Query( x << 1, l, mid, segL, segR ) + Query( x << 1 | 1, mid + 1, r, segL, segR ) ) % mod;
}

int QueryP( int u, int v ){
    int res = 0;
    while( top[u] != top[v] ){
        if( dep[top[u]] < dep[top[v]] )    swapp( u, v );
        res = ( ( res + Query( 1, 1, N, posL[top[u]], posL[u] ) ) % mod + mod ) % mod;
        u = fa[top[u]];
    }
    if( dep[u] >= dep[v] )    swapp( u, v );
    res = ( ( res + Query( 1, 1, N, posL[u], posL[v] ) ) % mod + mod ) % mod;
    return res;
}

int main( ){
    read( N );
    for( int i = 2, lst; i <= N; ++ i ){ read( lst ); pushEdge( lst, i ); }
    oneSearch( 1, 0 ); anotherSearch( 1, 1 );
    read( M );
    while( M -- > 0 ){
        int opt, u, x, k;
        read( opt );
        if( opt == 1 ){
            read( u ); read( x ); read( k );
            Modify( 1, 1, N, posL[u], posL[u], x );
            Modify( 1, 1, N, posL[u] + 1, posR[u], -k );
        }
        else{ read( u ); write( QueryP( 1, u ) ); putchar( '\n' ); }
    }
    return 0;
}

LOC 2020.11.20 - Prob. 4

Desc. & Link.

不想做了。

Problem. 1 - Junior Julian

模拟模拟模拟摸死 CTR 的母。

考场代码:

#include<cstdio>
namespace solveIt
{
void read(int &x)
{
    x=0;
    char c=getchar();
    int f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)    write(x/10);
    putchar(x%10+'0');
}
int Q;
int chkyearG(int mon) // 1:run 0:ping
{
    if(((mon%4==0)&&(mon%100!=0))||(mon%400==0))    return 1;
    else    return 0;
}
int chkyearJ(int mon) // 1:run 0:ping
{
    if(mon%4==0)    return 1;
    else    return 0;
}
int chkmontG(int mon,int yea) // 31: big,30: small,28/29: 2
{
    if(yea<0)    yea=-yea,yea--;
    if(mon==1||mon==3||mon==5||mon==7||mon==8||mon==10||mon==12)    return 31;
    else if(mon!=2)    return 30;
    else
    {
        if(chkyearG(yea))    return 29;
        else    return 28;
    }
}
int chkmontJ(int mon,int yea) // 31: big,30: small,28/29: 2
{
    if(yea<0)    yea=-yea,yea--;
    if(mon==1||mon==3||mon==5||mon==7||mon==8||mon==10||mon==12)    return 31;
    else if(mon!=2)    return 30;
    else
    {
        if(chkyearJ(yea))    return 29;
        else    return 28;
    }
}
void Main()
{
    read(Q);
    while(Q--)
    {
        int r;
        read(r);
        int days=1,months=1,years=-4713;
        int flag=0; // 0:Julian,1:Gregorian
        while(true)
        {
            if(years==0)    years=1;
            if(days==1&&months==10&&years==1582)
            {
                if(r<=3)
                {
                    days+=r;
                    break;
                }
                else if(r>3&&r<20)
                {
                    days+=3;
                    r-=3;
                    days=15;
                    days+=r;
                    break;
                }
                else
                {
                    flag=1;
                    days=1;
                    r-=21;
                    months++;
                }
            }
            if(days+r<=(flag?chkmontG(months,years):chkmontJ(months,years)))
            {
                days+=r;
                break;
            }
            days=1;
            r-=(flag?chkmontG(months,years):chkmontJ(months,years));
            months++;
            if(months==13)    months=1,years++;
        }
        if(years>0)    printf("%d %d %d\n",days,months,years);
        else    printf("%d %d %d BC\n",days,months,-years);
    }
}
}
int main()
{
//    freopen("D:/csp-s(windows)/julian/julian3.in","r",stdin);
//    freopen("D:/my.out","w",stdout);
    freopen("julian.in","r",stdin);
    freopen("julian.out","w",stdout);
    solveIt::Main();
    return 0;
}
/*
There are 365 days in a ping year.
There are 366 days in a run year.
There are 4714 ping years in -4713(BC) to 1582
There are 1582 run years in -4713(BC) to 1582
There are 2299622 days in -4713(BC) to 1582
There are 3571 ping years BC
There are 1142 yun years BC
There are 1721387 days in BC
*/

下来重写:

#include <cstdio>

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 ); 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' );
}

int MonthDays( int x ){
    if( x == 1 || x == 3 || x == 5 || x == 7 || x == 8 || x == 10 || x == 12 )    return 31;
    else if( x == 2 )    return 28;
    else    return 30;
}

bool checkRJ( int x ){ return ! ( x % 4 ); }
bool checkRG( int x ){ return ( ( ! ( x % 4 ) ) && ( x % 100 ) ) || ( ! ( x % 400 ) ); }

int Q;

void GetData( LL& dBC, LL& dAJ, LL& dAG, LL& dFK ){
    for( int i = 4712; ~ i; -- i )  dBC += 365 + checkRJ( i );
    dAJ = dBC; dAG = dBC;
    for( int i = 1; i <= 1581; ++ i )    dAJ += 365 + checkRJ( i );
    for( int i = 1; i <= 1582; ++ i )    dAG += 365 + checkRG( i );
    dFK = dAJ + 365;
    for( int i = 1; i <= 9; ++ i )  dAJ += MonthDays( i );
    dAJ += 4;
}

int fuckCTR( int x, int y ){
    if( y < 0 )    ++ y;
    if( x != 2 )    return MonthDays( x );
    if( y <= 1582 ) return MonthDays( x ) + checkRJ( y );
    else    return MonthDays( x ) + checkRG( y );
}

int main( ){
    LL dBC = 0, dAJ = 0, dAG = 0, dFK = 0;
    /*
     * several key time frame:
     * 4713.1.1
     * 1582.10.5~14 ( don't exist )
     * 1582.10.4 Julian Calendar has been fucked
     * 1582.10.15 Gregorian Calendar has been implemented
     * dBC - 1.1.1 (Julian Day)
     * dAJ - 1582.10.15 (Julian Day)
     * dFK - 1583.1.1 (Imagine that the fucking 10 days exist)
     * dAG - 1583.1.1 (the fucking 10 days don't exist(the fucking problem's description))
    */
    GetData( dBC, dAJ, dAG, dFK );
    read( Q ); while( Q -- > 0 ){
        LL R; read( R );
        if( R < dBC ){
            int years = 4713 - R / ( 365 * 4 + 1 ) * 4; R %= ( 365 * 4 + 1 );
            if( R >= 366 ){
            R -= 366; years --;
            if( R >= 365 ){ R -= 365; years --; }
            if( R >= 365 ){ R -= 365; years --; }
            }
            int months = 1;
            while( R - fuckCTR( months, -years ) >= 0 ){ R -= fuckCTR( months, -years ); months ++; }
            int days = R + 1;
            printf( "%d %d %d BC\n", days, months, years );
            continue;
        }
        if( R >= dAJ )    R += 10;
        if( R >= dBC && R < dFK ){
            R -= dBC;
            int years = R / ( 365 * 4 + 1 ) * 4 + 1; R %= ( 365 * 4 + 1 );
            if( R >= 365 ){ R -= 365; years ++; }
            if( R >= 365 ){ R -= 365; years ++; }
            if( R >= 365 ){ R -= 365; years ++; }
            int months = 1;
            while( R - fuckCTR( months, years ) >= 0 ){ R -= fuckCTR( months, years ); months ++; }
            int days = R + 1;
            printf( "%d %d %d\n", days, months, years );
            continue;
        }
        if( R >= dFK ){
            R -= dBC; R += dAG - dFK;
            int years = R / ( 365 * 400 + 24 * 4 + 1 ) * 400 + 1; R %= ( 365 * 400 + 24 * 4 + 1 );
            if( R >= 365 * 100 + 24 ){ R -= 365 * 100 + 24; years += 100; }
            if( R >= 365 * 100 + 24 ){ R -= 365 * 100 + 24; years += 100; }
            if( R >= 365 * 100 + 24 ){ R -= 365 * 100 + 24; years += 100; }
            years += R / ( 365 * 4 + 1 ) * 4; R %= ( 365 * 4 + 1 );
            if( R >= 365 ){ R -= 365; years ++; }
            if( R >= 365 ){ R -= 365; years ++; }
            if( R >= 365 ){ R -= 365; years ++; }
            int months = 1;
            while( R - fuckCTR( months, years ) >= 0 ){ R -= fuckCTR( months, years ); months ++; }
            int days = R + 1;
            printf( "%d %d %d\n", days, months, years );
        }
    }
    return 0;
}

码风飞跃的变化

Problem. 2 - Junior Zoo

把所有数或起来,然后计算需要买哪些饲料,然后统计 $1\texttt{ to }k$ 有哪些位没有被买,记为 $C$ 然后答案就是 $2^{C}-n$。

考场代码:

#include<cstdio>
#include<vector>
namespace solveIt
{
void read(int &x)
{
    x=0;
    char c=getchar();
    int f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)    write(x/10);
    putchar(x%10+'0');
}
const int MAXN=1e6+5;
int n,m,c,k,a[MAXN],vis[MAXN],fuc[MAXN];
struct ask
{
    int w,s;
    ask(int W=0,int S=0){w=W;s=S;}
}as[MAXN];
void Main()
{
    read(n),read(m),read(c),read(k);
    for(int i=1;i<=n;++i)    read(a[i]),fuc[a[i]]=1;
    for(int i=1;i<=m;++i)    read(as[i].w),read(as[i].s);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
//            printf("FU ");for(int i=1;i<=c;++i)    printf("%d ",vis[i]); puts("");
            if((a[i]>>as[j].w)&1)    vis[as[j].s]=1;//,printf("%d %d %d\n",a[i],as[j].w,as[j].s);
        }
    }
//    for(int i=1;i<=c;++i)    write(vis[i]),putchar(' '),write(i),putchar('\n');
    int up=(1<<k),ans=0;
    for(int s=0;s<up;++s)
    {
        if(fuc[s])    continue;
        bool flag=0,book=0;
        for(int i=1;i<=m;++i)
        {
            if((s>>as[i].w)&1)
            {
                flag=1;
                if(!vis[as[i].s])    book=1;
            }
            if(book)    break;
        }
        if(!flag)    ans++;
        else if(!book)    ans++;
    }
    write(ans);
}
}
int main()
{
    freopen("zoo.in","r",stdin);
    freopen("zoo.out","w",stdout);
    solveIt::Main();
    return 0;
}

下来改的:

#include<cstdio>
#include<algorithm>
using namespace std;
namespace solveIt
{
typedef unsigned long long ULL;
void read(int &x)
{
    x=0;
    char c=getchar();
    int f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void readull(ULL &x)
{
    x=0;
    char c=getchar();
    ULL f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)    write(x/10);
    putchar(x%10+'0');
}
void writeull(ULL x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)    writeull(x/10);
    putchar(x%10+'0');
}
const int MAXN=1e6+5;
int n,m,c,k,len;
ULL a[MAXN],all,pri[MAXN];
bool fuc[70],buc[MAXN];
struct ask
{
    int w,s;
    ask(int W=0,int S=0){w=W;s=S;}
}as[MAXN];
void Main()
{
    read(n),read(m),read(c),read(k);
    for(int i=1;i<=n;++i)
    {
        readull(a[i]);
        all|=a[i];
    }
    for(int i=1;i<=m;++i)
    {
        read(as[i].w);
        read(as[i].s);
        pri[i]=as[i].s;
    }
    sort(pri+1,pri+1+m);
    len=unique(pri+1,pri+1+m)-pri-1;
    for(int i=1;i<=m;++i)    as[i].s=lower_bound(pri+1,pri+1+len,as[i].s)-pri;
    for(int i=1;i<=m;++i)
    {
        if((all>>as[i].w)&1)    buc[as[i].s]=1;
    }
    for(int i=1;i<=m;++i)
    {
        if(!buc[as[i].s])    fuc[as[i].w]=1;
    }
    int tot=0;
    for(int i=0;i<k;++i)
    {
        if(!fuc[i])    ++tot;
    }
    if(tot==64)
    {
        if(n==0)    puts("18446744073709551616");
        else
        {
            ULL ans=0,two=1;
            for(int i=1;i<=63;++i)    two<<=1;
            ans+=two;
            ans-=n;
            ans+=two;
            writeull(ans);
        }
    }
    else    writeull((1ull<<tot)-n);
}
}
int main()
{
    // freopen("zoo.in","r",stdin);
    // freopen("zoo.out","w",stdout);
    solveIt::Main();
    return 0;
}

Problem. 3 - Senior Call

这儿

考场代码: (Loneliness)

下来改的:

#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;
}

Problem. 4 - Senior Snakes

先讲个 $\texttt{70pts}$ 的做法。没脑子吃,然后回来判断。所有大样例有些多一的代码改一下就有。

正解应该就是利用序列单调性把 $\texttt{70pts}$ 的暴力的线段树改成其他的东西。

目前还没做出来,可以欣赏一下我考场的 186 行 SGST(含义见代码)。

考场代码:

(upd:这份代码下面是 $\texttt{70pts}$ 的代码:)

#include<cstdio>
#include<algorithm>
using namespace std;
namespace solveIt
{
void read(int &x)
{
    x=0;
    char c=getchar();
    int f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')    f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)    write(x/10);
    putchar(x%10+'0');
}
struct node
{
    int val,pos;
    node(int V=0,int P=0)
    {
        val=V;
        pos=P;
    }
    bool operator<(const node&ano)const
    {
        if(val==ano.val)    return pos<ano.pos;
        else    return val<ano.val;
    }
};
const int MAXN=1e6+6;
int n,a[MAXN];
struct TREE
{
node mnx[MAXN*4],mxx[MAXN*4];
void upd(int x)
{
    mnx[x]=min(mnx[x<<1],mnx[x<<1|1]);
    mxx[x]=max(mxx[x<<1],mxx[x<<1|1]);
}
void build(int x,int l,int r)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
        upd(x);
    }
    else
    {
        mnx[x]=node(a[l],l);
        mxx[x]=node(a[l],l);
    }
}
void ins(int x,int l,int r,int pos,int val)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        if(mid>=pos)    ins(x<<1,l,mid,pos,val);
        else    ins(x<<1|1,mid+1,r,pos,val);
        upd(x);
    }
    else
    {
        mnx[x].val+=val;
        mxx[x].val+=val;
    }
}
node findmin(int x,int l,int r,int fr,int ba)
{
    if(l>ba||r<fr)    return 1e9;
    if(l>=fr&&r<=ba)    return mnx[x];
    else
    {
        int mid=(l+r)>>1;
        return min(findmin(x<<1,l,mid,fr,ba),findmin(x<<1|1,mid+1,r,fr,ba));
    }
}
node findmax(int x,int l,int r,int fr,int ba)
{
    if(l>ba||r<fr)    return 0;
    if(l>=fr&&r<=ba)    return mxx[x];
    else
    {
        int mid=(l+r)>>1;
        return max(findmax(x<<1,l,mid,fr,ba),findmax(x<<1|1,mid+1,r,fr,ba));
    }
}
} tmx,tmn;
void Main()
{
    int t;
    read(t);
    int flag=1;
    while(t--)
    {
        read(n);
        if(flag==1)
        {
            for(int i=1;i<=n;++i)    read(a[i]);
            flag=0;
        }
        else
        {
            for(int i=1,p,v;i<=n;++i)
            {
                read(p);
                read(v);
                int tmxv=tmx.findmax(1,1,n,p,p).val;
                int tmnv=tmn.findmin(1,1,n,p,p).val;
                tmx.ins(1,1,n,p,-tmxv);
                tmx.ins(1,1,n,p,v);
                tmn.ins(1,1,n,p,-tmnv);
                tmn.ins(1,1,n,p,v);
            }
            for(int i=1;i<=n;++i)    a[i]=tmx.findmax(1,1,n,i,i).val;
//            for(int i=1;i<=n;++i)    write(a[i]),putchar(' '); puts("");
        }
        tmx.build(1,1,n);
        tmn.build(1,1,n);
        int ans=n;
        while(true)
        {
            node nowmax=tmx.findmax(1,1,n,1,n);
            tmx.ins(1,1,n,nowmax.pos,-nowmax.val);
            node secmax=tmx.findmax(1,1,n,1,n);
            tmx.ins(1,1,n,nowmax.pos,nowmax.val);
            
            node nowmin=tmn.findmin(1,1,n,1,n);
            tmn.ins(1,1,n,nowmin.pos,-nowmin.val+1e9);
            node secmin=tmn.findmin(1,1,n,1,n);
            tmn.ins(1,1,n,nowmin.pos,nowmin.val-1e9);
//            printf("nmx=(%d %d),nmn=(%d %d),smx=(%d %d),smn(%d %d)\n",nowmax.pos,nowmax.val,
//                nowmin.pos,nowmin.val,secmax.pos,secmax.val,secmin.pos,secmin.val);
            if(((nowmax.val-nowmin.val<secmin.val)
                &&((nowmax.val-nowmin.val>secmax.val)
                    ||(nowmax.val-nowmin.val==secmax.val&&nowmax.pos>secmax.pos)))
                ||(((nowmax.val-nowmin.val==secmin.val)&&(nowmax.pos<secmin.val))
                    &&((nowmax.val-nowmin.val>secmax.val)
                        ||((nowmax.val-nowmin.val==secmax.val)&&(nowmax.pos>secmax.pos))))
                ||(nowmax.val-nowmin.val>secmin.val)
                ||((nowmax.val-nowmin.val==secmin.val)&&(nowmax.pos>secmin.pos)))
            {
                tmn.ins(1,1,n,nowmax.pos,-nowmin.val);
                tmn.ins(1,1,n,nowmin.pos,-nowmin.val+1e9);
                tmx.ins(1,1,n,nowmax.pos,-nowmin.val);
                tmx.ins(1,1,n,nowmin.pos,-nowmin.val);
                ans--;
            }
            else    break;
        }
        write(ans);
        putchar('\n');
    }
}
}
int main()
{
//    freopen("D:/csp-s(windows)/snakes/snakes3.in","r",stdin);
    freopen("snakes.in","r",stdin);
    freopen("snakes.out","w",stdout);
    solveIt::Main();
    return 0;
}
/*
SGST - Strong-Guy Segment Tree
*/

$\texttt{70pts}$

#include<cstdio>
#include<algorithm>
using namespace std;
namespace solveIt
{
void read(int &x)
{
    x=0;
    char c=getchar();
    int f=1;
    while(c<'0'||c>'9')
    {
        if(c=='-')  f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    x*=f;
}
void write(int x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
struct node
{
    int val,pos;
    node(int V=0,int P=0)
    {
        val=V;
        pos=P;
    }
    bool operator<(const node&ano)const
    {
        if(val==ano.val)    return pos<ano.pos;
        else    return val<ano.val;
    }
};
const int MAXN=1e6+6;
int n,ans,a[MAXN];
struct TREE
{
node mnx[MAXN*4],mxx[MAXN*4];
int siz,tp;
TREE()
{
    siz=0;
}
void upd(int x)
{
    mnx[x]=min(mnx[x<<1],mnx[x<<1|1]);
    mxx[x]=max(mxx[x<<1],mxx[x<<1|1]);
}
void build(int x,int l,int r)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
        upd(x);
    }
    else
    {
        mnx[x]=node(a[l],l);
        mxx[x]=node(a[l],l);
    }
}
void ins(int x,int l,int r,int pos,int val)
{
    if(l^r)
    {
        int mid=(l+r)>>1;
        if(mid>=pos)    ins(x<<1,l,mid,pos,val);
        else    ins(x<<1|1,mid+1,r,pos,val);
        upd(x);
    }
    else
    {
        mnx[x].val+=val;
        mxx[x].val+=val;
    }
}
node findmin(int x,int l,int r,int fr,int ba)
{
    if(l>ba||r<fr)  return 1e9;
    if(l>=fr&&r<=ba)    return mnx[x];
    else
    {
        int mid=(l+r)>>1;
        return min(findmin(x<<1,l,mid,fr,ba),findmin(x<<1|1,mid+1,r,fr,ba));
    }
}
node findmax(int x,int l,int r,int fr,int ba)
{
    if(l>ba||r<fr)  return 0;
    if(l>=fr&&r<=ba)    return mxx[x];
    else
    {
        int mid=(l+r)>>1;
        return max(findmax(x<<1,l,mid,fr,ba),findmax(x<<1|1,mid+1,r,fr,ba));
    }
}
void del(node one)
{
    if(tp==1)    ins(1,1,n,one.pos,-one.val);
    else    ins(1,1,n,one.pos,-one.val+1e9);
    siz--;
}
void buildemp(int f,int n)
{
    build(1,1,n);
    siz=n;
    tp=f;
}
};
struct treetoheap
{
TREE tmx,tmn;
node findmax(int l,int r)
{
    return tmx.findmax(1,1,n,l,r);
}
node findmin(int l,int r)
{
    return tmn.findmin(1,1,n,l,r);
}
void ins(node one)
{
    tmx.siz++;
    tmn.siz++;
    tmx.ins(1,1,n,one.pos,one.val);
    if(tmn.findmin(1,1,n,one.pos,one.pos).val==(int)1e9)    tmn.ins(1,1,n,one.pos,one.val-1e9);
    else    tmn.ins(1,1,n,one.pos,one.val);
}
void del(node one)
{
    tmx.del(one);
    tmn.del(one);
}
void buildemp(int n)
{
    tmx.buildemp(1,n);
    tmn.buildemp(0,n);
}
int siz()
{
    return tmx.siz;
}
} wer;
bool dfs()
{
    if(wer.siz()==2)    return 1;
    int f=0;
    while(wer.siz()>=3)
    {
        int siz=wer.siz();
        node nowmin=wer.findmin(1,n),nowmax=wer.findmax(1,n);
        wer.del(nowmin);
        wer.del(nowmax);
        node secmin=wer.findmin(1,n);
        wer.ins(node(nowmax.val-nowmin.val,nowmax.pos));
//        puts("\n/********************************************/");
//        printf("(%d %d %d)\n",secmin.val,secmin.pos,wer.siz());
//        for(int i=1;i<=n;++i)    printf("%d ",wer.findmax(i,i).val);puts("");
//        puts("/********************************************/\n");
        if(nowmax.val-nowmin.val<secmin.val)
        {
            if(!dfs())
            {
                ans=siz-1;
                return 1;
            }
            else if(f)
            {
                ans=siz;
                return 1;
            }
            else
            {
                ans=siz;
                return 0;
            }
        }
        ++f;
    }
    ans=1;
    return 1;
}
void Main()
{
    int t;
    read(t);
    int flag=1;
    while(t--)
    {
        if(flag==1)
        {
            read(n);
            for(int i=1;i<=n;++i)   read(a[i]);
            flag=0;
        }
        else
        {
            int tmp;
            read(tmp);
            for(int i=1,p,v;i<=tmp;++i)
            {
                read(p);
                read(v);
                a[p]=v;
            }
        }
        if(n==3)
        {
            if(a[1]+a[2]<=a[3])    puts("1");
            else    puts("3");
            continue;
        }
        wer.buildemp(n);
        ans=n;
        bool WASTED=dfs();
        write(ans);
        putchar('\n');
        WASTED=!WASTED;
    }
}
}
int main()
{
//    freopen("snakes/snakes4.in","r",stdin);
//    freopen("snakes/fuck.out","w",stdout);
//    freopen("snakes.in","r",stdin);
//    freopen("snakes.out","w",stdout);
    solveIt::Main();
    return 0;
}
/*
SGST - Strong-Guy Segment Tree
*/

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;
}