重学后缀数组 — LOJ 2122 「NOI2015」品酒大会

0 说在之前

jt 学长说的没错,SA 果然是写一次忘一次……

于是这次重新学了一次,发现之前的 Blog 问题比较多,于是重写一次算了

实际上这些东西就是变相重写 Oi Wiki 后缀数组那一页,不过是写给自己的罢了

Continue reading “重学后缀数组 — LOJ 2122 「NOI2015」品酒大会”

Codeforces Contest #1325 解题报告 & 题目大意

A EhAb AnD gCd

题目大意

给定一个整数 $x(2\leq x \leq 10^9)$

求 $a,b$ 满足 $\gcd(a,b) + \operatorname{lcm}(a,b) = x$

多组解输出任意一个

Code

这有啥说的,随便找个质因数让后输出 $p, x$ 就可以

// Woshiluo Luo<[email protected]>
// 2020/03/14 22:36:48

#include <cmath>
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

int t, x;

int main() {
    scanf( "%d", &t );
    while( t -- ) {
        scanf( "%d", &x );
        int sq = std::sqrt(x) + 1;
        for( int i = 1; i <= sq; i ++ ) {
            if( x % i == 0 ) {
                int tmp = ( x / i ) - 1;
                if( tmp == 0 )
                    continue;
                printf( "%d %d\n", i, tmp * i );
                break;
            }
        }
    }
}

B CopyCopyCopyCopyCopy

题目大意

给你一个长度为 $n$ 的数列 $a$, 现在你可以复制无数个 $a$ 接到原来的数列后面

问这样接完后,最长严格上升子序列的长度

Code

因为可以接无限次,所以直接输出数字个数就可以了

// Woshiluo Luo<[email protected]>
// 2020/03/14 22:42:47
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 1e5 + 1e4;

int t;
int n;
int a[N];

int main() {
    scanf( "%d", &t );
    while( t -- ) {
        scanf( "%d", &n );
        for( int i = 1; i <= n; i ++ ) {
            scanf( "%d", &a[i] );
        }
        std::sort( a + 1, a + n + 1 );
        int cnt = 0, la = 0;
        for( int i = 1; i <= n; i ++ ) {
            if( i == 1 || a[i] != la ) {
                la = a[i];
                cnt ++;
            }
        }
        printf( "%d\n", cnt );
    }
}

C Ehab and Path-etic MEXs

题目大意

给一棵节点数为 $n$ 的树

现在你要给每条边一个边权,要求:

  • 边权为一个整数 $v(0\leq v \leq n – 2)$
  • 每条边的边权不能和别的边的相同

现在定义 $MEX(u,v)$ 为 $u,v$ 之间的最短简单路径上没有出现的边权中的最小值

请给出一种边权方案,最小化 $\max{MEX(u,v)}$

思路

考虑每条边会被算进去多少次,记为每条边的贡献

贡献越大给越大的边就可以了

实际上可以更加简化,因为无论如何一定有一个 $MEX \geq 2$

所以我们尽可能避免 $0,1,2$ 出现在一条边上即可

随便找一个度数 $\geq 2$ 的点,把这三个数字放上去即可

如果没有这种点,那么怎么摆都会有一条 $MEX=n-1$ 的路径

Code

// Woshiluo Luo<[email protected]>  
// 2020/03/14 22:52:18
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 1e5 + 1e4;

// Edge Start
struct edge {
    int to, next, val;
} e[ N << 1 ];
int ehead[N], ecnt;
inline void add_edge( int now, int to, int val ) {
    ecnt ++;
    e[ecnt].to = to;
    e[ecnt].next = ehead[now];
    e[ecnt].val = val;
    ehead[now] = ecnt;
}
// Edge End

struct node { long long val; int id; } max[N];
bool cmp( node _a, node _b ) { return _a.val < _b.val; }

int n, x, y;
int son[N], val[N];

void dfs( int now, int la ) {
    son[now] = 1;
    for( int i = ehead[now]; i; i = e[i].next ) {
        if( e[i].to == la )
            continue;
        dfs( e[i].to, now );
        son[now] += son[ e[i].to ];
        max[ e[i].val ].val = ( 1LL * son[ e[i].to ] * ( n - son[ e[i].to ] ) );
    }
}

int main() {
#ifdef woshiluo
    freopen( "c.in", "r", stdin );
    freopen( "c.out", "w", stdout );
#endif
    scanf( "%d", &n );
    for( int i = 1, u, v; i < n; i ++ ) {
        max[i].id = i;
        scanf( "%d%d", &u, &v );
        add_edge( u, v, i );
        add_edge( v, u, i );
    }

    dfs( 1, 0 );

    std::sort( max + 1, max + n, cmp );

    for( int i = 1; i < n; i ++ ) {
        val[ max[i].id ] = i - 1;
    }

    for( int i = 1; i < n; i ++ ) {
        printf( "%d\n", val[i] );
    }
}

D Ehab the Xorcist

给定两个整数 $u,v$ 求最短的数列 $a$ 满足

  • $$ \sum a = v $$
  • xor 和为 $u$

多解输出任意一个

无解输出 $-1$

题目大意

这题感觉的 C 简单啊

满足 $xor$ 容易,满足 $v$ 的话需要难度

考虑最后 $xor$ 的结果受每个二进制位的和的奇偶性影响

因此可以确定每一位的奇偶性

然后随便搞一下满足 $v$ 就可以了

Code

// Woshiluo Luo<[email protected]>
// 2020/03/14 23:39:20
#include <cstdio>
#include <cstdlib>
#include <cstring>

#include <vector>
#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

unsigned long long u, v;
int bit_u[110], bit_less[110], bit_cnt[110];
std::vector<unsigned long long> ans;

inline void noans() {
    printf( "-1\n" );
    exit(0);
}

void to_bit( unsigned long long a, int bit[] ) {
    int cnt = 0;
    while( a ) {
        bit[ ++ cnt ] = a & 1;
        a >>= 1;
    }
}

int main() {
    scanf( "%llu%llu", &u, &v );
    if( v < u )
        noans();

    to_bit( v - u, bit_less );
    to_bit( u, bit_u );

    for( int i = 1; i <= 100; i ++ ) {
        bit_cnt[i] = bit_u[i];
    }
    int p = 64;
    int cnt = 0;
    while( p ) {
        bit_cnt[p] += ( cnt - ( cnt & 1 ) );
        cnt = cnt & 1;
        if( bit_less[p] )
            cnt ++;
        p --; cnt <<= 1;
    }

    if( cnt != 0 )
        noans();

    bool flag = true;
    while( flag ) {
        flag = false;
        unsigned long long out = 0;
        unsigned long long p = 1;
        for( int i = 1; i <= 64; i ++, p <<= 1 ) {
            if( bit_cnt[i] ) {
                out = out | p;
                bit_cnt[i] --;
                flag = true;
            }
        }
        if( flag )
            ans.push_back(out);
    }

    cnt = ans.size();
    printf( "%d\n", cnt );
    for( int i = 0; i < cnt; i ++ ) {
        printf( "%llu ", ans[i] );
    }

}

E Ehab’s REAL Number Theory Problem

题目大意

给你一个长度为 $n$ 的数列,保证数列中每个数字有不超过 $7$ 个因数

请求出最短的子序列,使子序列成绩为完全平方数

思路

这题比 F 题难吧…

首先由 保证数列中每个数字有不超过 $7$ 个因数 得每个数不会有超过两个 $1$ 和其本身以外的质因数

然后我们可以先把每个数里的完全平方数因子先除掉,因为这些不会对答案造成影响

这样下来每个数的因子只有 1 和其本身以及两个质因数(前提是这个数不是质数)

然后建图,如果这个数是质数,将其和 $1$ 连接,如过不是将其的两个质因数连接

剩下就是求最小环

Code

// Woshiluo Luo<[email protected]>
// 2020/03/15 22:08:53

#include <cmath>
#include <cstdio>
#include <cstring>

#include <queue>
#include <vector>
#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 1e6 + 1e4;
const int M = 1100;
const int INF = 0x3f3f3f3f;

int n, ans = INF;
int dis[N], fa[N];
bool marked[N], pool[N];

// Edge Start
struct edge {
    int cur, to, next;
} e[ N << 1 ];
int ehead[N], ecnt = 1;
inline void add_edge( int now, int to ) {
    ecnt ++;
    e[ecnt].cur = now;
    e[ecnt].to = to;
    e[ecnt].next = ehead[now];
    ehead[now] = ecnt;
}
// Edge End

int bfs( int st ) {
    memset( dis, INF, sizeof(dis) );
    memset( fa, 0, sizeof(fa) );
    std::queue<int> q;
    q.push(st); dis[st] = 0;
    while( !q.empty() ) {
        int cur = q.front(); q.pop();
        for( int i = ehead[cur]; i; i = e[i].next ) {
            if( e[i].to == fa[cur] )
                continue;

            if( dis[ e[i].to ] >= INF ) {
                fa[ e[i].to ] = cur;
                dis[ e[i].to ] = dis[cur] + 1;
                q.push( e[i].to );
            }
            else {
                return dis[cur] + dis[ e[i].to ] + 1;
            }
        }
    }
    return INF;
}

int main() {
    scanf( "%d", &n );
    for( int i = 1, x; i <= n; i ++ ) {
        scanf( "%d", &x );
        std::vector<int> a;
        int cnt = std::sqrt(x) + 1;
        while(cnt > 1) {
            int cnt_pow = cnt * cnt;
            while( x % cnt_pow == 0 )
                x /= cnt_pow;
            cnt --;
        }
        if( x == 1 )
            chk_Min( ans, 1 );
        if( pool[x] )
            chk_Min( ans, 2 );
        pool[x] = true;
        int tmp = std::sqrt(x);
        for( int j = 2; j <= tmp; j ++ ) {
            if( x % j == 0 ) {
                a.push_back(j);
                a.push_back( x / j );
                break;
            }
        }
        if( a.size() == 0 ) {
            add_edge( 1, x );
            add_edge( x, 1 );
        }
        else if( a.size() != 0 ) {
            add_edge( a[0], a[1] );
            add_edge( a[1], a[0] );
        }
    }

    if( ans != INF ) {
        printf( "%d\n", ans );
        return 0;
    }

    for( int i = 1; i <= 1000; i ++ ) {
        if( ehead[i] )
            chk_Min( ans, bfs(i) );
    }

    if( ans >= INF )
        ans = -1;

    printf( "%d\n", ans );
}

F Ehab’s Last Theorem

题目大意

给定一个 $n$ 个节点的图

令 $glo=\lceil\sqrt{n}\rceil$

你要么找到一个长度和 $glo$ 相等的环

要么找到一个比 $glo$ 小的独立集

Code

先试图找环,找不到肯定有独立集

// Woshiluo Luo<[email protected]>  
// 2020/03/15 15:49:32
#include <cstdio>
#include <cstring>

#include <stack>
#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 1e5 + 1e4;
const int M = 2e5 + 1e4;

int n, m, need;

int dep[N];
bool vis[N];
std::stack<int> st;

// Edge Start
struct edge { int to, next; } e[ M << 1 ];
int ehead[N], ecnt;
inline void add_edge( int now, int to ) {
    ecnt ++;
    e[ecnt].to = to;
    e[ecnt].next = ehead[now];
    ehead[now] = ecnt;
}
// Edge End


void dfs( int now ) {
    st.push(now);
    dep[now] = st.size();
    for( int i = ehead[now]; i; i = e[i].next ) {
        if( dep[ e[i].to ] == 0 )
            dfs( e[i].to );
        else if( dep[now] - dep[ e[i].to ] + 1 >= need ) {
            int size = dep[now] - dep[ e[i].to ] + 1;
            printf( "%d\n%d\n", 2, size );
            for( int i = 1; i <= size; i ++ ) {
                printf( "%d ", st.top() );
                st.pop();
            }
            exit(0);
        }
    }
    if( !vis[now] ) {
        for( int i = ehead[now]; i; i = e[i].next ) {
            vis[ e[i].to ] = true;
        }
    }
    st.pop();
}

int main() {
#ifdef woshiluo
    freopen( "f.in", "r", stdin );
    freopen( "f.out", "w", stdout );
#endif
    scanf( "%d%d", &n, &m );
    while( need * need < n )
        need ++;

    while( m -- ) {
        int u, v;
        scanf( "%d%d", &u, &v );
        add_edge( u, v ); add_edge( v, u );
    }

    dfs(1);

    printf( "%d\n", 1 );
    int cnt = 0;
    for( int i = 1; i <= n; i ++ ) {
        if( vis[i] == false ) {
            printf( "%d ", i );
            cnt ++;
        }
        if( cnt == need )
            break;
    }
}

Codeforces Contest #1312 解题报告 & 部分翻译

比赛链接:Educational Codeforces Round 83 (Rated for Div. 2)

A Two Regular Polygons

1 题目大意

给你两个正多边形,一个 $n$ 条边 $A$ ,一个 $m$ 条边 $B$

问 $B$ 有没有可能被 $A$ 包含且所有顶点都与 $A$ 的某一个节点重合

有输出 YES ,否则输出 NO

2 Code

判 $n \bmod m = 0$ 就可以了

#include <cstdio>

int T;
int n, m;

int main() {
    scanf( "%d", &T );
    while( T -- ) {
        scanf( "%d%d", &n, &m );
        printf( "%s\n", ( n % m == 0 )? "YES": "NO" );
    }
}

B Bogosort

1 题目大意

给你一个长度为 $n$ 的序列 $a$, 你可以将 $a$ 重新排序,使其满足 $j – a_j \neq i – a_i$

输出任何一个即可

保证有解

$n \leq 100$

2 思路

$$
\begin{aligned}
j – a_j & \neq i – a_i \\
j – i & \neq a_j – a_i
\end{aligned}
$$

直接从大到小输出

3 Code

// Woshiluo Luo<[email protected]>  
// 2020/03/09 22:40:32 
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 110;

int T;
int n;
int a[N];

int main() {
    scanf( "%d", &T );
    while( T -- ) {
        scanf( "%d", &n );
        for( int i = 1; i <= n; i ++ ) {
            scanf( "%d", &a[i] );
        }
        std::sort( a + 1, a + n + 1 );
        for( int i = n; i >= 1; i -- ) {
            printf( "%d ", a[i] );
        }
        printf( "\n" );
    }
}

C Adding Powers

1 题目大意

给你两个长度为 $n$ 的数列 $a,v$

其中 $a$ 通过输入提供,$v$ 初始所有值为 $0$

接下来,对于第 $i$ 次操作(从 $0$ 计数),你可以

  • 选择任意一个 $v_i$ 增加 $k^i$
  • 什么都不做

问你是否通过多次操作后,使 $v$ 变成 $a$

能输出 YES ,否则输出 NO

2 思路

首先对于每个数字 $a$, 考虑其是否可以变成多个 $k^i$ 的和(不能有重复的 $i$)

能的话算出来,看看有没有和之前重复的,有就不可能

不能的话直接没有可能

3 Code

// Woshiluo Luo<[email protected]>
// 2020/03/09 22:58:45
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 110;

int T;
int n;
long long k;
bool vis[N];
long long a[N];

inline void wrong() {
    printf( "NO\n" );
}

inline void right() {
    printf( "YES\n" );
}

void calc() {
    for( int i = 1; i <= n; i ++ ) {
        if( a[i] == 0 ) {
            continue;
        }
        long long cur = a[i];
        int cnt = 1;
        while( cur ) {
            int tmp = cur % k;
            if( tmp > 1 ) {
                wrong();
                return ;
            }
            if( tmp == 1 ) {
                if( vis[cnt] == false )
                    vis[cnt] = true;
                else {
                    wrong();
                    return ;
                }
            }
            cur /= k;
            cnt ++;
        }
    }
    right();
}

int main() {
    scanf( "%d", &T );
    while( T -- ) {
        memset( vis, 0, sizeof(vis) );

        scanf( "%d%lld", &n, &k );
        for( int i = 1; i <= n; i ++ ) {
            scanf( "%lld", &a[i] );
        }

        calc();
    }
}

D Count the Arrays

1 题目大意

你需要寻找这样的数列个数

  • 有 $n$ 个元素
  • 每个数字都是 $1$ 到 $m$ 之间的整数
  • 只有恰好一对数字相等
  • 数列先严格递增,再严格递减

个数对 $998244353$ 取模后输出

$2 \leq n \leq m \leq 2 \times 10^5$

2 思路

式子题,看 Code 去

3 Code

// Woshiluo Luo<[email protected]>
// 2020/03/09 23:21:18
#include <cstdio>
#include <cstring>

#include <algorithm>

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

const int N = 2e5 + 1e4;
const int mod = 998244353;

inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline void add_eq( int &a, int b ) { a = ( a + b ) % mod; }
inline void mul_eq( int &a, int b ) { a = ( 1LL * a * b ) % mod; }

int ksm( int a, int p ) {
    int res = 1;
    while(p) {
        if( p & 1 )
            res = mul( res, a );
        a = mul( a, a );
        p >>= 1;
    }
    return res;
}

inline int get_inv( int a ) { return ksm( a, mod - 2 ); }

int n, m, sum, ans;
int fact[N], inv[N];

void init() {
    fact[1] = 1;
    for( int i = 2; i <= m; i ++ ) {
        fact[i] = mul( fact[ i - 1 ], i );
    }
    inv[m] = get_inv( fact[m] );
    for( int i = m - 1; i >= 1; i -- ) {
        inv[i] = mul( inv[ i + 1 ], i + 1 );
    }
    inv[0] = 1;
    fact[0] = 1;
}

// Get C^a_b
inline int C( int a, int b ) {
    if( a <= 0 )
        return 1;
    if( b <= 0 )
        return 1;
    return mul( mul( fact[b], inv[ b - a ] ), inv[a] );
}

int main() {
#ifdef woshiluo
    freopen( "d.in", "r", stdin );
    freopen( "d.out", "w", stdout );
#endif
    scanf( "%d%d", &n, &m );
    init();

    for( int i = n - 1; i <= m; i ++ ) {
        add_eq( sum, mul( C( n - 3, i - 2 ), mul( m - i + 1, n - 2 ) ) );
    }

    for( int i = 2; i < n; i ++ ) {
        add_eq( ans, mul( sum, C( i - 2, n - 3 ) ) );
    }

    printf( "%d\n", ans );
}

E Array Shrinking

1 题目大意

这好像是个原题

给你一个长度为 $n$ 的数列 $a$

如果 $a_i = a_{i + 1}$, 那么这两个数可以合并成 $a_i + 1$

问最小数列长度

$1 \leq a_i \leq 1000, 1 \leq n \leq 500$

2 思路

区间 dp

设 $f_{i,j}$ 为 $i$ 到 $j$ 最小长度

$merged_{i,j}$ 为 $i$ 到 $j$ 合并出来的数字

然后就是标准板子了

3 Code

// Woshiluo Luo<[email protected]>
// 2020/03/10 15:50:48
#include <cstdio>
#include <cstring>

#include <algorithm>

const int N = 510;

template<class T>
T Min( T _a, T _b ) { return _a < _b? _a: _b; }
template<class T>
T Max( T _a, T _b ) { return _a > _b? _a: _b; }
template<class T>
T chk_Min( T &_a, T _b ) { return _a = (_a < _b? _a: _b); }
template<class T>
T chk_Max( T &_a, T _b ) { return _a = (_a > _b? _a: _b); }

int n;
int a[N];
int f[N][N], merged[N][N];

int main() {
#ifdef woshiluo
    freopen( "e.in", "r", stdin );
    freopen( "e.out", "w", stdout );
#endif
    scanf( "%d", &n );
    for( int i = 1; i <= n; i ++ ) {
        scanf( "%d", &a[i] );
        f[i][i] = 1;
        merged[i][i] = a[i];
    }
    for( int len = 2; len <= n; len ++ ) {
        for( int left = 1, rig = len; rig <= n; left ++, rig ++ ) {
            f[left][rig] = rig - left + 1;
            for( int mid = left; mid < rig; mid ++ ) {
                int &f_left = f[left][mid], &f_rig = f[ mid + 1 ][rig],
                &merge_left = merged[left][mid], &merge_rig = merged[ mid + 1 ][rig];
                chk_Min( f[left][rig], f_left + f_rig );
                if( f_left == 1 && f_rig == 1 && merge_left == merge_rig ) {
                    f[left][rig] = 1;
                    merged[left][rig] = merge_left + 1;
                }
            }
        }
    }
    printf( "%d\n", f[1][n] );
}

AGC 034 F RNG and XOR

题目连接: https://atcoder.jp/contests/agc034/tasks/agc034_f

道理我都懂,可是不看题解不到啊……

$$\newcommand \xor{\mathbin{\mathbf{xor}}}$$

1 题意

给定一个数字 $n$

对于 $0 \cdots 2^n – 1$ 中每个数字都有一个 $a_i$

现在有一个数 $X$, 一开始为 $0$

每一次操作会随机选择一个数字 $i$ (其中 $0 \leq i \leq 2^n – 1$,选中 $i$ 的概率为 $\frac{a_i}{\sum a}$)

然后令 $X = X \xor i$

问使 $X=i$ 的期望次数

2 思路

先令
$$p_i=\frac{a_i}{\sum a}$$

令 $f_i$ 表示从 $i$ 到 $0$ 的期望次数,这和从 $0$ 到 $i$ 显然是一样的

接着有一个非常显然的式子

$$
\begin{aligned}
f_i &= f_{i \xor j} \times p_j + 1 ( i \neq 0 )\\
f_i – 1 &= f_{i \xor j } \times p_j ( i \neq 0 )
\end{aligned}
$$

然后我们就可以得到这个式子

$$ (f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \xor (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
= ( x, f_1 – 1, f_2 – 1, \cdots, f_{2^{n-1}} – 1, f_{2^n-1} – 1 ) $$

但是 $x$ 是未知的

注意到 $\sum p = 1$ 所以左边右边的和是相等的

所以
$$
\begin{aligned}
x &= \sum_{i=0}^{2^n-1} f_i – \sum_{i=1}^{2^n-1} f_i – 1 \\
&= f_0 + 2^n – 1
\end{aligned}
$$

$$
(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \xor (p_0, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
= ( f_0 + 2^n – 1, f_1 – 1, f_2 – 1, \cdots, f_{2^{n-1}} – 1, f_{2^n-1} -1 )
$$

我们需要的使其中两个式子没有未知数

注意到如果使 $p_0 = p_0 – 1$

那么右边每一个数都会减去 $f_i$

$$
(f_0, f_1, f_2, \cdots, f_{2^{n-1}}, f_{2^n-1}) \xor (p_0 – 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\ = ( 2^n – 1, – 1, – 1, \cdots, -1, -1 )
$$

于是就可以放进 FWT 里直接做

然后你就发现这东西跑出来是错的……

为什么呢


$$
\begin{aligned}
\mathbf{P} & = (p_0 – 1, p_1, p_2, \dots, p_{2^{n-1}}, p_{2^n}) \\
\mathbf{A} & = ( 2^n – 1, – 1, – 1, \cdots, -1, -1 )
\end{aligned}
$$

因为 $\mathbf{P}$ 和 $\mathbf{A}$ FWT 后第一位都是 $0$

没有办法倒推出来 $f_0$

但是可以发现 $f$ 的值是可以平移的

$$ f_i + k = \sum_{i=0}^{2^n-1} p_j(f_{i \xor j} + k) + 1 = \sum_{i=0}^{2^n-1} f_{i \xor j} \times p_j + k + 1 $$

那么我们又知道 $f_i = 0$

所以将 $f$ 每一位都减去 $f_0$ 即可

3 Code

#include <cstdio> 

const int N = 1 << 20;
const int mod = 998244353;

int n;
int a[N], b[N], p[N], sum;

inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline void add_eq( int &a, int b ) { a = ( a + b ) % mod; }
inline void mul_eq( int &a, int b ) { a = ( 1LL * a * b ) % mod; }

int ksm( int a, int p ) {
    int res = 1;
    while( p ) {
        if( p & 1 )
            res = mul( res, a );
        a = mul( a, a );
        p >>= 1;
    }
    return res;
}
inline int inv( int a ) { return ksm( a, mod - 2 ); }

void XOR( int *f, int len, int x = 1 ) {
    for( int o = 2, k = 1; o <= len; o <<= 1, k <<= 1 ) {
        for( int i = 0; i < len; i += o ) {
            for( int j = 0; j < k; j ++ ) {
                add_eq( f[ i + j ], f[ i + j + k ] );
                f[ i + j + k ] = add( f[ i + j ], add( - f[ i + j + k ], - f[ i + j + k ] ) );
                mul_eq( f[ i + j ], x ); mul_eq( f[ i + j + k ], x );
            }
        }
    }
}

int main() {
#ifdef woshiluo
    freopen( "F.in", "r", stdin );
    freopen( "F.out", "w", stdout );
#endif
    scanf( "%d", &n );
    n = 1 << n;
    for( int i = 0; i < n; i ++ ) {
        scanf( "%d", &a[i] );
        sum += a[i];
        b[i] = mod - 1;
    }
    sum = inv(sum);
    for( int i = 0; i < n; i ++ ) {
        p[i] = mul( a[i], sum );
    }

    b[0] = n - 1; p[0] -= 1;
    XOR( b, n ); XOR( p, n );
    for( int i = 0; i < n; i ++ ){
        mul_eq( b[i], inv( p[i] ) );
    }
    XOR( b, n, inv(2) );
    for( int i = 0; i < n; i ++ ) {
        printf( "%d\n", ( ( add( b[i], -b[0] ) + mod ) % mod ) );
    }
}

NOIP 2017 提高组 Day2 T3 列队

题目链接: https://loj.ac/problem/2319

Also: https://www.luogu.com.cn/problem/P3960

1 思路

很明显,这个题目需要我们维护 $ n + 1 $ 个序列

这些序列要支持

  • 查询第 $k$ 个
  • 删除第 $k$ 个
  • 在末尾插入

动点脑子的话,树状树组上二分也是可行的,反正我不会

不动脑子的话, Splay 下高问题也不大

2 实现细节

如果把每个节点都单独储存的话,空间 $O(n + m)$ 承受不能

但是考虑到总修改次数很小,而且 Splay 维护的很多节点都是连续的

所以可以让 Splay 把连续的区间当作一个节点

空间问题就解决了

这样时间大概是 $O(q\log m)$ 的…

但是 Splay 的常数巨大…

于是就被 LibreOJ 无情卡常了

大概大力卡还是能过,但是我懒得搞了,反正 Luogu 上过了

3 Code

#include <cstdio>

inline long long seg_len( long long left, long long rig ) { return rig - left + 1; }
template <class T>
T read() {
	T res = 0; char x = getchar();
	while( x < '0' || x > '9' ) {
		x = getchar();
		continue;
	}
	while( x >= '0' && x <= '9' ) {
		res = res * 10 + x - '0';
		x = getchar();
	}
	return res;
}

int st_cnt, st[30];
template <class T>
void write( T res ) {
	while( res ) {
		st_cnt ++;
		st[ st_cnt ] = res % 10;
		res /= 10;
	}
	while( st_cnt ) {
		putchar( st[ st_cnt ] + '0' );
		st_cnt --;
	}
}

const int N = 3e5 + 1e4;

int n, m, q;

struct splay_node {
	splay_node *son[2], *fa;
	long long left, rig, size;
	splay_node( long long _left = 0, long long _rig = 0 ) {
		son[0] = son[1] = fa = 0; size = seg_len( _left, _rig );
		left = _left, rig = _rig;
	}
	void push_up() {
		this -> size = ( this -> son[0]? this -> son[0] -> size: 0 ) 
			+ ( this -> son[1]? this -> son[1] -> size: 0 ) 
			+ seg_len( left, rig );
	}
	bool get_son() { 
		if( this -> fa && this -> fa -> son[1] ) 
			return ( this -> fa -> son[1] ) == this;
		return 0;
	}
	inline long long len() { return seg_len( this -> left, this -> rig ); }
};

struct Splay {
	splay_node *rt;
	void init( long long left, long long rig ) {
		splay_node *c = new splay_node( left, rig );
		rt = c;
		rt -> push_up();
	}
	void rotate( splay_node *cur ) {
		bool kind = cur -> get_son();
		splay_node *tmp_fa = cur -> fa;
		if( tmp_fa == 0 ) 
			return ;
		if( tmp_fa -> fa == 0 ) 
			rt = cur;
		else 
			tmp_fa -> fa -> son[ tmp_fa -> get_son() ] = cur;
		cur -> fa = tmp_fa -> fa;
		tmp_fa -> son[kind] = cur -> son[ kind ^ 1 ];
		if( tmp_fa -> son[kind] )
			tmp_fa -> son[kind] -> fa = tmp_fa;
		cur -> son[ kind ^ 1 ] = tmp_fa; tmp_fa -> fa = cur;
		tmp_fa -> push_up(); cur -> push_up();
	}
	void splay( splay_node *from, splay_node *to = 0 ) {
		while( from -> fa != to ) {
			splay_node *fa = from -> fa;
			if( fa -> fa != to ) 
				rotate( ( fa -> get_son() ) == ( from -> get_son() )? fa: from );
			rotate(from);
		}
	}
	splay_node* find_kth( long long k ) {
		if( k == 0 ) 
			return 0;
		splay_node *cur = rt;
		while( cur ) {
			if( cur -> son[0] ) {
				if( cur -> son[0] -> size < k )
					k -= cur -> son[0] -> size;
				else {
					cur = cur -> son[0];
					continue;
				}
			}
			if( k <= cur -> len() ) 
				break;
			k -= cur -> len();
			if( cur -> son[1] ) {
				cur = cur -> son[1];
				continue;
			}
			return 0;
		}
		return cur;
	}
	splay_node* split_kth( long long k ) {/*{{{*/
		splay_node *cur = find_kth(k); splay(cur);
		if( cur -> son[0] ) 
			k -= cur -> son[0] -> size;
		k = ( cur -> left ) + k - 1;
		splay_node *ori_left = cur -> son[0], *ori_rig = cur -> son[1],
			*left = 0, *rig = 0, *mid;
		mid = new splay_node( k, k );
		if( cur -> left <= k - 1 ) {
			left = new splay_node( cur -> left, k - 1 );
			left -> fa = mid;
		}
		if( k + 1 <= cur -> rig ) {
			rig = new splay_node( k + 1, cur -> rig );
			rig -> fa = mid;
		}

		mid -> son[0] = left; mid -> son[1] = rig;
		splay_node *tmp_left = (left? left: mid), *tmp_rig = (rig? rig: mid);
		tmp_left -> son[0] = ori_left; tmp_rig -> son[1] = ori_rig;
		if( ori_left )
			ori_left -> fa = tmp_left; 
		if( ori_rig )
			ori_rig -> fa = tmp_rig;

		if( left ) 
			left -> push_up();
		if( rig ) 
			rig -> push_up();

		mid -> push_up();
		if( rt == cur ) 
			rt = mid;
		delete( cur );
		return mid;
	}/*}}}*/
	void del_kth( long long k ) {
		splay_node *cur = split_kth(k);
		splay_node *left = find_kth( k - 1 ), *rig = find_kth( k + 1 );
		if( left == 0 || rig == 0 ) {
			splay( cur );
			rt = ( cur -> son[ left == 0 ] ); rt -> fa = 0;
			delete( cur );
			return ;
		}
		splay( left ); splay( rig, left );
		delete( rt -> son[1] -> son[0] );
		rt -> son[1] -> son[0] = 0;
		rt -> son[1] -> push_up(); rt -> push_up();
	}
	void pull_up( splay_node *cur ) {
		if( cur ) {
			cur -> push_up();
			pull_up( cur -> fa );
		}
	}
	splay_node* push_back( long long val ) {
		splay_node *cur = rt;
		while( cur ) {
			if( cur -> son[1] ) {
				cur = cur -> son[1];
				continue;
			}
			cur -> son[1] = new splay_node( val, val );
			cur -> son[1] -> fa = cur;
			cur = cur -> son[1];
			break;
		}
		pull_up( cur );
		splay(cur);
		return cur;
	}
#ifdef debug
	void zxbl( splay_node *cur ) {
		if( cur == 0 ) 
			 return ;
		zxbl( cur -> son[0] );
		for( int i = cur -> left; i <= cur -> rig; i ++ ) 
			printf( "%2lld ", i );
		zxbl( cur -> son[1] );
	}
	void _zxbl() {
		zxbl( this -> rt );
	}
#endif
} T[N];

int main() {
#ifdef woshiluo
	freopen( "luogu.3960.in", "r", stdin );
	freopen( "luogu.3960.out", "w", stdout );
#endif
	n = read<int>(); m = read<int>(); q = read<int>();
	T[0].init( m, m );
	T[1].init( 1, m - 1 );
	for( int i = 2; i <= n; i ++ ){
		T[i].init( 1LL * ( i - 1 ) * m + 1, 1LL * i * m - 1 );
		T[0].push_back( 1LL * i * m );
	}
	while( q -- ) {
		int x, y;
		x = read<int>(); y = read<int>();
		if( y == m ) {
			long long cur = T[0].split_kth(x) -> left;
			write(cur);
			printf( "\n" );
			T[0].push_back(cur); T[0].del_kth(x);
		}
		else {
			Splay &tree = T[x];
			long long cur = tree.split_kth(y) -> left;
			write(cur);
			printf( "\n" );
			tree.push_back( T[0].split_kth(x) -> left ); tree.del_kth(y);
			T[0].push_back(cur); T[0].del_kth(x); 
		}
#ifdef debug
		printf( "%d %d\n", x, y );
		for( int i = 0; i <= n; i ++ ) {
			printf( "T[%d]: ", i );
			T[i]._zxbl();
			printf( "\n" );
		}
#endif
	}
}

Atcoder ARC 103 F Distance Sums

0 写在之前

这几天写的题目并不算少

然而这道题是唯一一个看过去傻了的题目…

应该以前听过,题面看着眼熟

可是死活不会做…

1 思路

基本上可以明确是到构造题

这里依赖两个性质:

树的中心的 $D$ 值是最小的

$$ D(fa) = D(x) – n + 2 \times size[i] $$

这个就是标准的树和树的重心的性质

可以推出来如果以重心做根,那么 $D$ 越大越在下面

所以尝试生成一颗以重心为根的树就行了

然后我发现我不会判否

结果发现最后生成出来树判断一次就行了…

2 实现

读入 $D$ 排序

从大到小依此根据上面的式子找父亲

最后判断一下是否正确就可以了

3 Code

// User: woshiluo
// Email: [email protected]
// Problem link: https://atcoder.jp/contests/arc103/tasks/arc103_d
// Comment: 
// Why the problem id is 'F', but the link is 'arc103_d'
// Interesting

#include <cstdio>
#include <cstdlib>

#include <map>
#include <algorithm>

const int N = 1e5 + 1e3;

int n;
int size[N], father[N];

struct node{
	int id;
	long long d;
} a[N];

std::map<long long, int> mp;

void wrong() {
	printf( "-1\n" );
	exit(0);
}
bool cmp( node _a, node _b ) { return _a.d < _b.d; }

int main() {
#ifdef woshiluo
	freopen( "F.in", "r", stdin );
	freopen( "F.out", "w", stdout );
#endif
	scanf( "%d", &n );
	for( int i = 1; i <= n; i ++ ) {
		scanf( "%lld", &a[i].d );
		a[i].id = i;
		size[i] = 1;
		mp[ a[i].d ] = i;
	}
	std::sort( a + 1, a + n + 1, cmp );
	int rt_d, rt;
	rt = a[1].id; rt_d = a[1].d;
	for( int i = n; i > 1; i -- ) {
		int fa = mp[ a[i].d + 2LL * size[ a[i].id ] - n ];
		if( fa == 0 ) {
			wrong();
			return 0;
		} 
		size[fa] += size[ a[i].id ];
		father[ a[i].id ] = fa;
	}
	for( int i = 1; i <= n; i ++ ) {
		if( i == rt ) 
			continue;
		 rt_d -= size[i];
	}
	if( rt_d != 0 ) 
		wrong();

	for( int i = 1; i <= n; i ++ ) {
		if( i == rt ) 
			continue;
		printf( "%d %d\n", father[i], i );
	}
}

HDU 4507 恨7不成妻

思路

裸的要死的数位 DP 题目

一眼看过去,平方和,有点懵

但是维护平方和也是老套路了

维护 $sum$, $cnt$, $pow_sum$ 就可以了

代码

代码写的着急,会有一些不太好看

#include <cstdio>

const int mod = 1e9 + 7;

inline int add( int a, int b ) { return ( a + b ) % mod; }
inline int mul( int a, int b ) { return ( 1LL * a * b ) % mod; }
inline int power( int a ) { return mul( a, a ); }

int T, max_len;
long long left, rig;
int _count[110][2][10][10][10], _sum[110][2][10][10][10], _pow[110][2][10][10][10];
int max_count[110][2][10][10][10], max_sum[110][2][10][10][10], max_pow[110][2][10][10][10];
bool _vis[110][2][10][10][10];
int a[110];

inline int length( int cur, int len ) {
	int res = cur;
	while( len > 0 ) {
		res = mul( 10, res );
		len --;
	}
	return res;
}

struct node {
	int len, cur, seven, rem, base_rem;
	bool max;
	int& count() {
		if( max ) 
			return max_count[len][seven][rem][cur][base_rem];
		return _count[len][seven][rem][cur][base_rem];
	}
	int& sum() {
		if( max )
			return max_sum[len][seven][rem][cur][base_rem];
		return _sum[len][seven][rem][cur][base_rem];
	}
	int& pow() {
		if( max ) 
			return max_pow[len][seven][rem][cur][base_rem];
		return _pow[len][seven][rem][cur][base_rem];
	}
	bool& vis() {
		return _vis[len][seven][rem][cur][base_rem];
	}
	void print() {
//		printf( "%d %d %d %d %d %d\n", len, cur, seven, rem, base_rem, max );
//		printf( "%d %d %d\n", count(), sum(), pow() );
	}
};

int dfs( node cur ) {
	if( ( ! cur.max ) && cur.vis() )
		return cur.pow();
	if( cur.len == 0 ) {
		if( cur.seven || cur.rem == 0 || cur.base_rem == 0 ) {
			cur.count() = cur.sum() = cur.pow() = 0;
			return cur.pow();
		}
		cur.count() = 1; cur.sum() = cur.cur; cur.pow() = power( cur.cur );
		return cur.pow();
	}
	int &cur_count = cur.count();
	int &cur_sum = cur.sum();
	int &cur_pow = cur.pow();
	if( cur.max ) 
		cur_count = cur_sum = cur_pow = 0;
	for( int i = 0; i <= ( cur.max? a[cur.len]: 9 ); i ++ ) {
		node nxt = cur;
		nxt.len -= 1; nxt.seven = ( cur.seven || ( i == 7 ) ); nxt.cur = i; nxt.max = ( cur.max && i == a[cur.len] );
		nxt.rem = ( cur.rem * 10 + i ) % 7; nxt.base_rem = ( cur.base_rem + i ) % 7;
		dfs( nxt );
		cur_count = add( cur_count, nxt.count() );
		cur_sum = add( cur_sum, add( mul( length( cur.cur, cur.len ), nxt.count() ), nxt.sum() ) );
		cur_pow = add( cur_pow, add( nxt.pow(), add( mul( nxt.count(), power( length( cur.cur, cur.len ) ) ), 
						mul( mul( 2, length( cur.cur, cur.len ) ), nxt.sum() ) ) ) );
	}
	if( cur.max == false )
		cur.vis() = true;
	cur.print();
	return cur_pow;
}

int sum( long long _a ) {
	if( _a == 0 ) 
		return 0;
	if( _a < 0 )
		return 0;
	max_len = 0;
	while( _a ) { 
		a[ ++ max_len ] = _a % 10;
		_a /= 10;
	}
//	printf( "ans: %d\n", dfs( (node){ max_len, 0, 0, 0, 0, true } ) );
	return dfs( (node){ max_len, 0, 0, 0, 0, true } );
}

int main() {
#ifdef woshiluo
	freopen( "hdu.4507.in", "r", stdin );
	freopen( "hdu.4507.out", "w", stdout );
#endif
	scanf( "%d", &T );
	while( T -- ) {
		scanf( "%lld%lld", &left, &rig );
		if( left == 0 ) 
			printf( "%d\n", ( sum(rig) + mod ) % mod );
		else 
			printf( "%d\n", ( add( sum(rig), - sum( left - 1 ) ) + mod ) % mod );
	}
}

Luogu P3177 [HAOI 2015] 树上染色

题目链接: https://www.luogu.com.cn/problem/P3177

0 说在之前

树形 DP 要么模板题,要么神仙题

这个题就属于不太正常的

1 思路

$f_{i,j}$

其中 $i$ 代表当前节点

$j$ 代表子树中选择 $j$ 个黑点,所能对答案产生的贡献

有了这个思路,后面的东西就是标准的树上背包了

2 Code

#include <cstdio>
#include <cstring>

inline int Min(int a, int b) { return a < b? a : b; }
inline long long Max(long long a, long long b) { return a > b? a : b; }

const int N = 2100;

int n, m;
int size[N];

// Edge Start
struct edge {
    int to, val, next;
} e[N << 1];
int ehead[N], ecnt;

inline void add_edge(int now, int to, int val) {
    ecnt ++;
    e[ecnt].to = to;
    e[ecnt].val = val;
    e[ecnt].next = ehead[now];
    ehead[now] = ecnt;
}
// Edge End

// DP Start
long long f[N][N];
void dp(int now, int la) {
    size[now] = 1;
    f[now][0] = f[now][1] = 0;
    for(int i = ehead[now]; i; i = e[i].next) {
        if( e[i].to == la )
            continue;
        dp(e[i].to, now);
        size[now] += size[ e[i].to ];
        for(int j = Min(size[now], m); j >= 0; j--) {
            for(int k = 0; k <= Min(size[ e[i].to ], j); k++) {
                if( f[now][ j - k ] == -1 )
                    continue;
                long long val = 1LL * ( 1LL * k * ( m - k ) + 1LL * ( size[ e[i].to ] - k ) * ( n - m - size[ e[i].to ] + k ) ) * e[i].val;
                f[now][j] = Max( f[now][j], f[now][j - k] + f[ e[i].to ][k] + val);
            }
        }
    }
}
// DP End

int main() {
#ifdef woshiluo
    freopen("luogu.3177.in", "r", stdin);
    freopen("luogu.3177.out", "w", stdout);
#endif
    scanf("%d%d", &n, &m);
    for(int i = 1, u, v, w; i < n; i++) {
        scanf("%d%d%d", &u, &v, &w);
        add_edge(u, v, w);
        add_edge(v, u, w);
    }

    memset(f, -1, sizeof(f));
    dp(1, 0);

    printf("%lld", f[1][m]);
    fclose(stdin);
    fclose(stdout);
}

PKUWC 2020(2019) 游记

0 序

晚上十一点半的机房,Woshiluo 正悠闲地考虑着第二天该做的题目

说着他一瞬想起来自己似乎报名过 PKUWC

「过去几年都没有人通过的,今年夏令营整个新疆都没过,果然还是白忙活吧」

话虽这么说,Woshiluo 还是打开了报名网站

「喵喵喵,过了?」

1 Day -2 出发

在经过严密的安检过后

我终于到达了乌鲁木齐站三层

比较有意思是,我的检票口貌似是 A1

然而 A1 前面的人都已经坐满了

我走到夹层,和绝大多数高铁站一样,这个地方是用来骗钱的

不过也就意味着,这里的人会少一点

稍作调整之后,车站广播也响起来了

随着缓慢的人群,我通过了检票口,到达了火车上

2 Day -1 火车上的短暂时光

乌鲁木齐铁路局所销售物品的昂贵已经是有很久历史的事情了

所以如果上车的时候犯懒没有买足吃的东西的话

可以到大站下去购买以尽可能的减少损失

这趟列车绝大部分地区都是没有信号的地方

所以基于手机的娱乐基本上是没有了

Kindel 和随身携带的纸制书可能是最有效的消磨时间方式

所以我在火车上过了一遍绿书,并读完了「樱花庄的宠物女孩」的本篇及番外

我的上铺和下铺都是年轻人,一个考研,一个去内地学习技术

总而言之,大家也都有自己的前途

3 Day 0 慌忙的首都一天

在早上十点,火车到达了北京西站

我从地铁口出来,联系上了 sbr,我从北京西站到国家图书馆再换乘到海淀黄庄

两个人见面稍微寒暄了一小阵,就到星巴克里谈人生

到了快要报道的时间,我们从海淀黄庄坐到北京大学东门 D 出口,从 D 口出来后约 5min 就可以到达报道的地方

我前面比没有几个人,进去之后签到的一栏旁边还有一个 是否使用 NOI Linux

看到这一栏时,就体会到了 PKU 的良心,更不要提后面还有 PKU 食堂 150 CNY 的饭卡

和 sbr 打车到国贸大厦,惊奇的发下自己把一些东西落在了报到处…

算了不重要

然后在和 sbr 吃完午饭后,我坐地铁从国贸到东大桥

经过 5min 的第六感导航,我顺利的到达了 Animate

这里的 Animate 和广东店装修风格好像啊

不过比广东店大了不少

时间超级紧迫,因为很明显,我需要在报道时间结束前回到报道处,拿回自己的东西

所以我就一目十行,随便扫了点 LoveLive! 和 碧蓝航线 的东西就飞速离开了

不过最意外的收获就是搞到了一本 「LoveLive! School idol diary」还是日文原版的

然后一路 Rush 到北京大学的报道处,偷偷的拿到了自己的东西

接着到了自己的酒店,真的离 PKU 好远啊…

稍微整理了一下在 Animate 的战果后跑去和 杨老师 — 我们的教练 会面

然后发现自己这边里所有地铁站都好远啊…

选了个最近的地铁站 — 上地站,然后惊奇的发现 13 号线几乎全是在地表的,我一开始以为这是轻轨,后来发现这东西叫城铁

从上地站出发,换乘两次到鼓楼,然后杨老师告诉我她到雍和宫了

于是从鼓楼出发,换乘一次到雍和宫

后面和老师吃了顿饭,然后和地铁关闭时间赛跑,换乘两次回到上地站

待我再回到酒店的时候,我人已经完全坏掉了

4 Day 1 稍有秩序的开幕

早上在酒店滴滴了 10min +,终与打到了车…

匆匆忙忙,到达开幕式所在场所

认识了几位大佬

开幕式大约介绍了一下北大,然后就散了

然后发现,没有地方休息的说

在经历了一阵混乱后,最后反正是在中关新园定了一间房

嘛,虽然贵的要死,不过明天也算是有地方休息了

反正今天是没有时间了

和教练一起在食堂吃了顿饭

在吃完 PKU 食堂的饭后,真的让人想上 PKU (不是

食堂饭又便宜又好吃,感谢 PKU 给的饭卡

然后跑到考场,Windows 考生使用 Windows 10,具体配置也不清楚

NOI Linux 考生使用的是 Dell 的笔记本,Intel i7,16G DDR4

多余的也没看,毕竟体验很优秀

T1

  • 离谱玩意儿,Subtask 1 随便搞搞就出来了
  • Subtask 2 推了好长时间…没有出来

T2

  • 全肝 T1 和 T3 了
  • 这道题目 Subtask 很多,认真写的花应该有至少 20pts

T3

  • Subtask 1 不用带脑子
  • Subtask 2 离线查分即可,没来的及写
  • 后面的 Subtask 不是我这种人该碰的

告辞 28pts 跑路

在 T1 莽的太久了, T2 T3 好多暴力没拿

中关新园离 pku 是真的近(

回去没有什么事情,就一直在颓废

5 Day 2 PKU 旅游日

面试

据说面试还有紧张到说不出话的

基本上面试文的问题都挺普通的

面试的时候会有一个预定的时间表

我想着我还没到时间,就去食堂买了个面包当早餐

结果回来发现已经到下一个了

无限尴尬

不过还好,面试老师并没有说什么

和一个天津大佬面基成功w

测试

T1

  • 跟着 Subtask 一步步走,基本上都能过
  • 说白了就是人口普查题
  • 我随便在考场上胡了个 $O(n\log^2(n))$ 的,反正过了

T2

  • 神仙,Subtask 1 跑路

T3

  • 最小割?
  • 有一说一,我上次敲网络流应该还是在中山
  • 弃了

最后 100 + 32 + 0 = 132pts

还算可以,基本上算是我的最大能力了(

晚上

回到酒店后,想起今天是冬至,便外卖了份饺子

然而是真的贵

吃完虽然没有吃饱,不过就这样子吧

然后颓废

6 Day 3

早早的就起来了

和 lk 爷爷面基,一起在楼下的包子铺吃了招行

两个人扯皮了好久

Orz lk 太强了

后来两个人打车去 PKU,中间 LK 在 THU 下了

我到了之后刚好开幕

我们教练家女儿生病了,于是我又双是一个人了

闭幕式

一开始大概就是领导讲话

然后就开始讲题

讲下来感觉自己还是很有希望 200 + 的

不过 Day 1 的时候智商下线了w

题倒是都还好,除了 Day 1 T3 完全没搞懂

剩下的都是能听懂但是不会写的(

然后颁奖

我觉得和我没什么关系,就把衣服拉链来开了,毕竟很热

然后惊奇的发现有个三等奖,结果上去的时候拉链没拉上www

晚上

后面发现闭幕式结束才 15 点

直接冲出去坐地铁到搜秀城w

然后再采购

然后就是回宾馆睡觉

晚上和 sbr 再面基

6 Day +1

上午和 Galaxy 见面

(或许是我这次唯一一个面基的成年人?)

然后带我逛了下 THU,顺便在 THU 吃了顿饭

和他聊了一阵子天,受益匪浅

正准备去机场

然后飞机延误了

7 Day +2

意外之喜

于是再去了趟 Animate,好好逛了一把

发现了好多之前没有发现的东西

大收获的说

8 Day +3

飞机又双延误了

告辞,火车真香

CSP 2019 游记

0 序

今年感觉相比较以往还是难了一些的

不过没有大家说的那么夸张倒是真的

不过自己貌似真的变菜了

0.1 吐槽

笔者考完 CSP 就会初中部回了一天

然后周二病到周五

一直在不发烧和低烧徘徊

但是一直咳嗽

咳得人 头疼 + 胸口疼 + 嗓子疼

新疆地邪啊

1 Day 0

今年七十中并不给试机子(注:也有可能是 CCF/科协 不让)

罢了,也没什么看头,每年 11 月都会在七十中冻一下

中午大家一起吃饭

虽然想学习,最后还是放弃了

晚上翻来覆去睡不着,迷迷糊糊最后还是睡了

2 Day 1

2.0 序 – 清晨

早上一开门,哇,下雪了

无论这个比赛怎么改名下雪还是避免不了的

在寒冷的凌晨,我们又拿起武器,向着熟悉的战场再次走去

2.1 CSP – S2 Day 1

T3 是个啥啊?

一中学长们的校车因为各种各样的原因,8:25 才到

遇见了不少以前的熟人,SpinMry,dyf 大家好久不见了,一起加油吧!

匆匆忙忙赶进考场,熟练的敲下 .vimrc,慌的一批

到了时间,准点发题

一直很想问都 9102 年,新疆为什么还是不用压缩包加密这种可靠性高的多的方法

非要用电子教师软件传输,然后再匆匆忙忙断网

罢了,有人愿意举办就不错了

  • T1
    • 属实不带脑子的题目,上来一看基本上就知道从上往下模拟即可 $O(64)$
      不过确实有一种更为简单的办法,参见 Oi-wiki – 格雷码
      最后看数据范围注意到 unsigned long long,出题人太毒瘤了吧
  • T2
    • 随便一看就是瞎搞累计贡献,道理我都懂,这代码怎么写?
      考场上一顿胡写,树上瞎跳,然后写点优化
      写完一开样例3,114514 太臭了,一跑,爆栈了,手写递归去了
      这么一套下来,感觉要出事 $\text{flag}_1$
  • T3
    • 一看,神仙题,10pts 告辞
      等一下这个 10pts 为什么这么难调,时间到了,完蛋

2.2 午休

考完心态小崩,找 dyf 一起快乐

两个人一起吃了饭,然后开始互相吐槽各自的学校

聊完天,两个人有一起走到七十中,路上还在吐槽某大佬的的迷路属性,结果到了七十中校门口就碰到了某大佬

2.3 CSP – J2

先在二楼和 dyf 与 caq 一起聊天 CSP 本质面基大会无疑

然后快考试了才去了三楼,运气可以抽到了比较暖和的位置

  • T1
    • 哇这么水的题目真的有出的必要吗…
  • T2
    • 一开始没看懂在搞什么,还想着 CSP – J2 都考平衡树了?
      后来一看 $price_i \leq 1000$ ,那没事了,多队列维护开溜
  • T3
    • 一眼背包,倒是不知道怎么搞
      弃了
  • T4
    • 最短路题目,怎么搞随缘

一出考场发现 2 道原题过分了啊

T3 我竟然做过,傻了

3 Day 2

3.1 CSP – S2 Day 2

又见熟人,不过因为 Day1 的心态,所以就没有过多闲聊

遇到郭老师,问他 Day 1 爆栈的问题

满口都是什么

「机子环境不重要的啊」
「全国只有两个省份提供了 NOI Linux,还都是虚拟机,别的都是 Windows 」

我看加个「七十中断电和七十中无关」都可以郭老师三连了

顺带一提,Day2 T2 内存 1GB 我们考试环境总共就 1GB 内存,可把我给整乐了

算了,XJ 这个占全国 $\frac{1}{6}$ 领土的小省份还是不要想别的了吧

  • T1
    • 什么玩意儿,神仙,跳
  • T2
    • $O(n^3)$ 是很显然了,就 $f_{i,j}$ 为以 $i$ 结束,向前 $j$ 位的最优答案就行了
      然后对 $i$ 考虑优化,考虑 $i$ 相等的情况下有两种状态 $j,k$,若 $j < k$ 且 $f_{i,j} < f_{i,k}$ 那么 $k$ 状态显然可以舍去
      不就是个弱化版斜率优化吗
  • T3
    • $O(n^2)$ 的点不用带脑子,敲就完事了
      链的点是不带脑子,可是我又双没时间了

3.2 下午

随便在一中食堂买了桶泡面

然后就会机房和大家一起聊天

高二的学长们很大一部分都要退役了,说不好真的是最后一次见了…

祝各位安好

After

一点 CSP 过后的小记

Day +2

选手程序出了

找了几个数据在 NOI Linux 上测试了一下

顺带一提,是远程测试,是真的延迟高…

最后基本上都是

$100 + 55 + 0 + 0 + 64 + 40 = 259$

也不知道官方数据怎么样…

Day +5

斗胆看了下 Day1 T2

发现自己把

st[ st_cnt ] 

写成了

st_cnt

离谱

Day +14

正在去市一中的路上,突然看到有个群里说成绩出了

CCF 又写 Bug了(当然不排除故意的可能性)

一路狂奔跑到机房

拿到数据正准备测试,结果机房 DNS 锅了

看了一下发现 php-fpm 服务挂了

修复了之后开始评测,然后在大屏上直播

感觉还可以,评测期间基本上就是在骂测了 1min + 但是却 0 pts 的憨憨

Day +16

想了想,还是有想停课冲冬令营的想法

稍作思考也许就会出结果了吧…

回去文化课实际上感觉还可以,基本上也都听得懂

勉强在班里还算可以,不过在这个学校里想要上一中必须得很靠前啊…

一点小小的总结

今年可能比较去年可能会更加迷茫一些

去年的问题有一条今年还有的

  • 时间规划有一定问题

除此之外,能察觉到的主观问题还有

  • 思想方向有些许问题
  • 过于追梦,导致检查和部分暴力点未能做到

以下是一些客观原因

  • 提高组和入门组一起考太痛苦了,敲到最后在考场上不想写 值得一提的是,相似问题也在第一轮认证时有出现
  • 某名的感觉体力不太行…考试到最后手一直在冷(也有可能是七十中实在是太冷了)

顺带一提,我和同省大爷 StudyingFather 的意见一样

个人感觉我们今年的培训题目还是太偏了,考试虽然一直都在考,但是留于改题与总结的时间并不算长

不过我们也确实做不到跟上出题趋势这个大方向

希望我们会走的更好吧

「Jump up HIGH!!」

– Aqours

今年已经是我第三年学习 Oi 了

也许自己真的在实现最初的梦想

当时刚学 Oi 的时候,就一直在想,要是我能拿到 XJ 的第二块 Au 就好了

后来目标渐渐变了,变成很多奇奇怪怪的东西

回过头来,却发现自己也无意识中接近了最初的目标

不过还是很遥远就是了

不过人好歹还是要有梦想的,毕竟自己文化课也不算优秀

所以说,就当是有梦想了吧

虽然自己菜的真实

「私たち、 輝きたい!」

「LoveLive!SunShine!!」

Woshiluo
2019/12/04
乌鲁木齐市第一中学 小机房