2024牛客暑期多校训练营1

Preface

第一场牛客多校,直接被创飞了

开局本来舒畅无比,签了 A,C,H 后马上上机 Rush I,然后写了一坨东西后过了编译就交然后就过了

此时祁神给出了 J 题的做法,遂让机然后看榜上过的比较多的 B,D 两题,徐神开出了字符串 G 但感觉十分难写

然后后面我和徐神花样对着 B,D 两个题想办法却还是没能做动一点,祁神写 J 也被一堆细节创飞了

最后结束前堪堪调出了 J 题,让排名不至于太惨淡;徐神最后 1h 才开始写 G,最后写了 250 行在比赛结束后 30min 也过了这个题

后面看题解发现 B 题赛时的做法基本就差临门一脚了,D 题主要是没想到转化为前缀和来做,不然感觉能打出更好看的排名的说


A Bit Common

不难发现可以枚举最后有 \(p\) 个奇数,\(n-p\) 个偶数,则偶数怎么选都无所谓,奇数需要保证剩下的 \(m-1\) 位每一位都不能全为 \(1\)

后面那种可以经典容斥解决,复杂度 \(O(nm)\)

#include <bits/stdc++.h>

constexpr int $n = 5001;
constexpr int $m = 5001;

int p2[$n * $m], n, m, q;

int C[$n][$m];

int main() {
    std::ios::sync_with_stdio(false);
    std::cin >> n >> m >> q;
    for(int i = 0; i <= 5000; ++i) C[i][0] = 1;
    for(int i = 1; i <= 5000; ++i) for(int j = 1; j <= i; ++j)
        C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % q;
    p2[0] = 1;
    for(int i = 1; i <= 5000 * 5000; ++i) p2[i] = (p2[i - 1] << 1) % q;
    int64_t ans = 0;
    for(int p = 1; p <= n; ++p) {
        int64_t c1 = int64_t(C[n][p]) * p2[(m - 1) * (n - p)] % q;
        int64_t c2 = 0;
        for(int k = 0; k < m; ++k) {
            if(~k & 1) c2 = (c2     + int64_t(C[m - 1][k]) * p2[p * (m - 1 - k)]    ) % q;
            else       c2 = (c2 + q - int64_t(C[m - 1][k]) * p2[p * (m - 1 - k)] % q) % q;
        }
        ans = (ans + c1 * c2) % q;
    }
    std::cout << (ans % q + q) % q << char(10);
    return 0;
}

A Bit More Common

这题在 A 题的基础上要求 \(\ge 2\) 的方案数,显然就是把 A 题中求出的答案减去恰好为 \(1\) 的方案数

对于有 \(p\ge 2\) 个奇数的情况,我们可以做一个 \(p\)\(m-1\) 列的表,其中每个元素都是 \(0\)\(1\)

现在要求每列都要有至少一个 \(0\);且不存在任意一行使得删去这行后剩下的每列依然有至少一个 \(0\)

显然我们现在关心那些只有一个 \(0\) 的列,我们把这些列称为关键列,同时每个关键列可以 cover 特定的一行

那么我们只要满足每行都被 cover 即可,考虑枚举关键列的数量 \(t\),令 \(g_{p,t}\) 表示用 \(t\) 个关键列去 cover \(p\) 行的方案数

考虑移除某一个关键列后剩下的局面可能有 \(p\) 或者 \(p-1\) 个被 cover 的行,则有转移:\(g_{i,j}=i\times (g_{i,j-1}+g_{i-1,j-1})\)

剩下的 \(m-1-t\) 列每列都有 \(2^p-p-1\) 种方案可以任选,因此式子就很好写了

复杂度仍为 \(O(nm)\),但由于模数可变常数巨大,搞了个 Atcoder 的 MINT 板子才过

#include <bits/stdc++.h>

namespace atcoder {

namespace internal {

// @param m `1 <= m`
// @return x mod m
constexpr long long safe_mod(long long x, long long m) {
    x %= m;
    if (x < 0) x += m;
    return x;
}

// Fast modular multiplication by barrett reduction
// Reference: https://en.wikipedia.org/wiki/Barrett_reduction
// NOTE: reconsider after Ice Lake
struct barrett {
    unsigned int _m;
    unsigned long long im;

    // @param m `1 <= m < 2^31`
    barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}

    // @return m
    unsigned int umod() const { return _m; }

    // @param a `0 <= a < m`
    // @param b `0 <= b < m`
    // @return `a * b % m`
    unsigned int mul(unsigned int a, unsigned int b) const {
        // [1] m = 1
        // a = b = im = 0, so okay

        // [2] m >= 2
        // im = ceil(2^64 / m)
        // -> im * m = 2^64 + r (0 <= r < m)
        // let z = a*b = c*m + d (0 <= c, d < m)
        // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
        // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
        // ((ab * im) >> 64) == c or c + 1
        unsigned long long z = a;
        z *= b;
#ifdef _MSC_VER
        unsigned long long x;
        _umul128(z, im, &x);
#else
        unsigned long long x =
            (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
#endif
        unsigned int v = (unsigned int)(z - x * _m);
        if (_m <= v) v += _m;
        return v;
    }
};

// @param n `0 <= n`
// @param m `1 <= m`
// @return `(x ** n) % m`
constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
    if (m == 1) return 0;
    unsigned int _m = (unsigned int)(m);
    unsigned long long r = 1;
    unsigned long long y = safe_mod(x, m);
    while (n) {
        if (n & 1) r = (r * y) % _m;
        y = (y * y) % _m;
        n >>= 1;
    }
    return r;
}

// Reference:
// M. Forisek and J. Jancina,
// Fast Primality Testing for Integers That Fit into a Machine Word
// @param n `0 <= n`
constexpr bool is_prime_constexpr(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 7 || n == 61) return true;
    if (n % 2 == 0) return false;
    long long d = n - 1;
    while (d % 2 == 0) d /= 2;
    constexpr long long bases[3] = {2, 7, 61};
    for (long long a : bases) {
        long long t = d;
        long long y = pow_mod_constexpr(a, t, n);
        while (t != n - 1 && y != 1 && y != n - 1) {
            y = y * y % n;
            t <<= 1;
        }
        if (y != n - 1 && t % 2 == 0) {
            return false;
        }
    }
    return true;
}
template <int n> constexpr bool is_prime = is_prime_constexpr(n);

// @param b `1 <= b`
// @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g
constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
    a = safe_mod(a, b);
    if (a == 0) return {b, 0};

    // Contracts:
    // [1] s - m0 * a = 0 (mod b)
    // [2] t - m1 * a = 0 (mod b)
    // [3] s * |m1| + t * |m0| <= b
    long long s = b, t = a;
    long long m0 = 0, m1 = 1;

    while (t) {
        long long u = s / t;
        s -= t * u;
        m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b

        // [3]:
        // (s - t * u) * |m1| + t * |m0 - m1 * u|
        // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
        // = s * |m1| + t * |m0| <= b

        auto tmp = s;
        s = t;
        t = tmp;
        tmp = m0;
        m0 = m1;
        m1 = tmp;
    }
    // by [3]: |m0| <= b/g
    // by g != b: |m0| < b/g
    if (m0 < 0) m0 += b / s;
    return {s, m0};
}

// Compile time primitive root
// @param m must be prime
// @return primitive root (and minimum in now)
constexpr int primitive_root_constexpr(int m) {
    if (m == 2) return 1;
    if (m == 167772161) return 3;
    if (m == 469762049) return 3;
    if (m == 754974721) return 11;
    if (m == 998244353) return 3;
    int divs[20] = {};
    divs[0] = 2;
    int cnt = 1;
    int x = (m - 1) / 2;
    while (x % 2 == 0) x /= 2;
    for (int i = 3; (long long)(i)*i <= x; i += 2) {
        if (x % i == 0) {
            divs[cnt++] = i;
            while (x % i == 0) {
                x /= i;
            }
        }
    }
    if (x > 1) {
        divs[cnt++] = x;
    }
    for (int g = 2;; g++) {
        bool ok = true;
        for (int i = 0; i < cnt; i++) {
            if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                ok = false;
                break;
            }
        }
        if (ok) return g;
    }
}
template <int m> constexpr int primitive_root = primitive_root_constexpr(m);

}  // namespace internal

}  // namespace atcoder


#include <cassert>
#include <numeric>
#include <type_traits>

namespace atcoder {

namespace internal {

#ifndef _MSC_VER
template <class T>
using is_signed_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value ||
                                  std::is_same<T, __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int128 =
    typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                  std::is_same<T, unsigned __int128>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using make_unsigned_int128 =
    typename std::conditional<std::is_same<T, __int128_t>::value,
                              __uint128_t,
                              unsigned __int128>;

template <class T>
using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                  is_signed_int128<T>::value ||
                                                  is_unsigned_int128<T>::value,
                                              std::true_type,
                                              std::false_type>::type;

template <class T>
using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                 std::is_signed<T>::value) ||
                                                    is_signed_int128<T>::value,
                                                std::true_type,
                                                std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<(is_integral<T>::value &&
                               std::is_unsigned<T>::value) ||
                                  is_unsigned_int128<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<
    is_signed_int128<T>::value,
    make_unsigned_int128<T>,
    typename std::conditional<std::is_signed<T>::value,
                              std::make_unsigned<T>,
                              std::common_type<T>>::type>::type;

#else

template <class T> using is_integral = typename std::is_integral<T>;

template <class T>
using is_signed_int =
    typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using is_unsigned_int =
    typename std::conditional<is_integral<T>::value &&
                                  std::is_unsigned<T>::value,
                              std::true_type,
                              std::false_type>::type;

template <class T>
using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                              std::make_unsigned<T>,
                                              std::common_type<T>>::type;

#endif

template <class T>
using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;

template <class T>
using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;

template <class T> using to_unsigned_t = typename to_unsigned<T>::type;

}  // namespace internal

}  // namespace atcoder

#include <cassert>
#include <numeric>
#include <type_traits>

#ifdef _MSC_VER
#include <intrin.h>
#endif

namespace atcoder {

namespace internal {

struct modint_base {};
struct static_modint_base : modint_base {};

template <class T> using is_modint = std::is_base_of<modint_base, T>;
template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;

}  // namespace internal

template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
struct static_modint : internal::static_modint_base {
    using mint = static_modint;

  public:
    static constexpr int mod() { return m; }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    static_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    static_modint(T v) {
        long long x = (long long)(v % (long long)(umod()));
        if (x < 0) x += umod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    static_modint(T v) {
        _v = (unsigned int)(v % umod());
    }
    static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v -= rhs._v;
        if (_v >= umod()) _v += umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        unsigned long long z = _v;
        z *= rhs._v;
        _v = (unsigned int)(z % umod());
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        if (prime) {
            assert(_v);
            return pow(umod() - 2);
        } else {
            auto eg = internal::inv_gcd(_v, m);
            assert(eg.first == 1);
            return eg.second;
        }
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static constexpr unsigned int umod() { return m; }
    static constexpr bool prime = internal::is_prime<m>;
};

template <int id> struct dynamic_modint : internal::modint_base {
    using mint = dynamic_modint;

  public:
    static int mod() { return (int)(bt.umod()); }
    static void set_mod(int m) {
        assert(1 <= m);
        bt = internal::barrett(m);
    }
    static mint raw(int v) {
        mint x;
        x._v = v;
        return x;
    }

    dynamic_modint() : _v(0) {}
    template <class T, internal::is_signed_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        long long x = (long long)(v % (long long)(mod()));
        if (x < 0) x += mod();
        _v = (unsigned int)(x);
    }
    template <class T, internal::is_unsigned_int_t<T>* = nullptr>
    dynamic_modint(T v) {
        _v = (unsigned int)(v % mod());
    }
    dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }

    unsigned int val() const { return _v; }

    mint& operator++() {
        _v++;
        if (_v == umod()) _v = 0;
        return *this;
    }
    mint& operator--() {
        if (_v == 0) _v = umod();
        _v--;
        return *this;
    }
    mint operator++(int) {
        mint result = *this;
        ++*this;
        return result;
    }
    mint operator--(int) {
        mint result = *this;
        --*this;
        return result;
    }

    mint& operator+=(const mint& rhs) {
        _v += rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator-=(const mint& rhs) {
        _v += mod() - rhs._v;
        if (_v >= umod()) _v -= umod();
        return *this;
    }
    mint& operator*=(const mint& rhs) {
        _v = bt.mul(_v, rhs._v);
        return *this;
    }
    mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }

    mint operator+() const { return *this; }
    mint operator-() const { return mint() - *this; }

    mint pow(long long n) const {
        assert(0 <= n);
        mint x = *this, r = 1;
        while (n) {
            if (n & 1) r *= x;
            x *= x;
            n >>= 1;
        }
        return r;
    }
    mint inv() const {
        auto eg = internal::inv_gcd(_v, mod());
        assert(eg.first == 1);
        return eg.second;
    }

    friend mint operator+(const mint& lhs, const mint& rhs) {
        return mint(lhs) += rhs;
    }
    friend mint operator-(const mint& lhs, const mint& rhs) {
        return mint(lhs) -= rhs;
    }
    friend mint operator*(const mint& lhs, const mint& rhs) {
        return mint(lhs) *= rhs;
    }
    friend mint operator/(const mint& lhs, const mint& rhs) {
        return mint(lhs) /= rhs;
    }
    friend bool operator==(const mint& lhs, const mint& rhs) {
        return lhs._v == rhs._v;
    }
    friend bool operator!=(const mint& lhs, const mint& rhs) {
        return lhs._v != rhs._v;
    }

  private:
    unsigned int _v;
    static internal::barrett bt;
    static unsigned int umod() { return bt.umod(); }
};
template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;

using modint998244353 = static_modint<998244353>;
using modint1000000007 = static_modint<1000000007>;
using modint = dynamic_modint<-1>;

namespace internal {

template <class T>
using is_static_modint = std::is_base_of<internal::static_modint_base, T>;

template <class T>
using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;

template <class> struct is_dynamic_modint : public std::false_type {};
template <int id>
struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};

template <class T>
using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;

}  // namespace internal

}  // namespace atcoder

using namespace std;
typedef atcoder::modint MINT;
constexpr int $n = 5001;
constexpr int $m = 5001;

int n, m, q;

MINT p2[$n * $m], C[$n][$m], f[$m], g[$m][$n], pw[$m];


int main() {
    std::ios::sync_with_stdio(false);
    std::cin >> n >> m >> q; MINT::set_mod(q);
    for(int i = 0; i <= max(n,m); ++i) C[i][0] = 1;
    for(int i = 1; i <= max(n,m); ++i) for(int j = 1; j <= i; ++j)
        C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
    p2[0] = 1;
    for(int i = 1; i <= n * m; ++i) p2[i] = p2[i - 1] * 2;
    for(int p = 1; p <= n; ++p) {
        for(int k = 0; k < m; ++k) {
            if(~k & 1) f[p]+=C[m - 1][k] * p2[p * (m - 1 - k)];
            else       f[p]-=C[m - 1][k] * p2[p * (m - 1 - k)];
        }
    }
    
    g[0][0]=1;
    
    for (int i=1;i<=n;++i) for (int j=1;j<=m;++j)
    g[i][j]=i*(g[i-1][j-1]+g[i][j-1]);
    
    MINT ans = 0;
    for(int p = 2; p <= n; ++p) {
        pw[0]=1;
        for (int i=1;i<=m;++i) pw[i]=pw[i-1]*(p2[p]-1-p);
        MINT c1 = C[n][p] * p2[(m - 1) * (n - p)];
        MINT c2 = f[p];
        for (int i=p;i<m;++i)
        c2-=C[m-1][i]*g[p][i]*pw[m-1-i];
        ans+=c1*c2;
    }
    std::cout << ans.val() << char(10);
    return 0;
}


Sum of Suffix Sums

签到,第 \(i\) 个数的贡献就是 \(i\times a_i\),直接处理即可

#include<cstdio>
#include<iostream>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005,mod=1e9+7;
int q,t,v,n,pfx[N];
int main()
{
	for (scanf("%d",&q);q;--q)
	{
		scanf("%d%d",&t,&v);
		n=n-t+1; pfx[n]=(pfx[n-1]+1LL*n*v%mod)%mod;
		printf("%d\n",pfx[n]);
	}
	return 0;
}

XOR of Suffix Sums

想到转换成前缀就很简单的一个题,但就是没往这方面想

\(pfx_i\) 为原序列的前缀和,则答案为 \(\bigoplus_{i=1}^n (pfx_n-pfx_{i-1})\) 的低 \(21\)

不妨按位考虑,假设当前考虑第 \(d\) 位,则如果当前的 \(pfx_n-pfx_{i-1}\) 这一位上为 \(1\),则 \((pfx_n-pfx_{i-1})\bmod 2^{d+1}\ge 2^d\)

不妨用树状数组维护 \(pfx_{i-1}\) 的个数,每次询问会固定 \(pfx_n\) 的值,此时合法的 \(pfx_{i-1}\) 的值在模 \(2^{d+1}\) 意义下就是一段/两段连续的区间

总复杂度 \(O(n\log^2 Mod)\)

#include<cstdio>
#include<iostream>
#include<vector>
#define RI register int
#define CI const int&
using namespace std;
const int N=500005;
int q,t,v,n,pfx[N];
class Tree_Array
{
	private:
		vector <int> bit; int lim;
	public:
		inline void init(CI n)
		{
			bit.assign(n+1,0); lim=n;
		}
		#define lowbit(x) (x&-x)
		inline void add(RI x,CI y)
		{
			for (++x;x<=lim;x+=lowbit(x)) bit[x]+=y;
		}
		inline int get(RI x,int ret=0)
		{
			for (++x;x;x-=lowbit(x)) ret+=bit[x]; return ret;
		}
		inline int query(CI l,CI r)
		{
			if (l>r) return 0; return get(r)-get(l-1);
		}
		#undef lowbit
}BIT[21];
inline void modify(CI x,CI mv)
{
	for (RI i=0;i<=20;++i)
	{
		int p=1<<i+1; BIT[i].add((p-x%p)%p,mv);
	}
}
int main()
{
	RI i; for (i=0;i<=20;++i) BIT[i].init(1<<i+1);
	for (scanf("%d",&q);q;--q)
	{
		scanf("%d%d",&t,&v);
		while (t--) modify(pfx[n-1],-1),--n;
		pfx[n+1]=pfx[n]+v; ++n;
		modify(pfx[n-1],1); int ans=0;
		for (i=0;i<=20;++i)
		{
			int p=1<<i+1,l=1<<i,r=p-1,cnt=0;
			l=(l-pfx[n]%p+p)%p; r=(r-pfx[n]%p+p)%p;
			if (l<=r) cnt=BIT[i].query(l,r);
			else cnt=BIT[i].query(l,p-1)+BIT[i].query(0,r);
			ans|=(cnt&1)<<i;
		}
		printf("%d\n",ans);
	}
	return 0;
}

Product of Suffix Sums

神秘防 AK 题,鉴定为弃疗


Career Path

题意什么鬼东西一大串,鉴定为弃疗


3/2 Square Strings

究极神秘科技题,徐神写了个 runs+SA 给淦过去了,狂码 250 行实属神人也

反正我是一点做不来,只能贴下徐神代码

#include <bits/stdc++.h>
int n, m, k;
char s[200001], t[200001];
namespace has {
    using namespace std;
    const int mod = 100'000'009, b = 117;
    int a[400005];
    int pw[400005], s[400005];
    inline void build(int n) {
        pw[0] = 1;

        for (int i = 1; i <= n; i++) {
            pw[i] = 1ll * pw[i - 1] * b % mod;
            s[i] = (s[i - 1] + 1ll * pw[i - 1] * a[i]) % mod;
        }
    }
    inline int query(int l, int r) {
        return s[r] < s[l - 1] ? s[r] + mod - s[l - 1] : s[r] - s[l - 1];
    }
    inline int lcs(int i, int j, int up = 1 << 30) {
        if (a[i] != a[j])
            return 0;

        if (i > j)
            swap(i, j);

        if (!i)
            return 0;

        if (1ll * query(1, i) * pw[j - i] % mod == query(j - i + 1, j))
            return i;

        int l = 2, r = min(i, up), ans = 1;

        if (1ll * query(i - r + 1, i) * pw[j - i] % mod == query(j - r + 1, j))
            return r;

        while (l <= r) {
            int mid = (l + r) >> 1;

            if (1ll * pw[j - i] * query(i - mid + 1, i) % mod ==
                    1ll * query(j - mid + 1, j))
                ans = mid, l = mid + 1;
            else
                r = mid - 1;
        }

        return ans;
    }
    inline int lcp(int i, int j, int up = 1 << 30) {
        if (a[i] != a[j])
            return 0;

        if (i > j)
            swap(i, j);

        if (j > n)
            return 0;

        int l = 2, r = min(up, n - j + 1), ans = 1;

        if (1ll * query(i, i + r - 1) * pw[j - i] % mod == query(j, j + r - 1))
            return r;

        while (l <= r) {
            int mid = (l + r) >> 1;

            if (1ll * pw[j - i] * query(i, i + mid - 1) % mod == query(j, j + mid - 1))
                ans = mid, l = mid + 1;
            else
                r = mid - 1;
        }

        return ans;
    }
} // namespace has

using has::lcp;
using has::lcs;
struct runs {
    int l, r, p;
    bool operator<(const runs &j) const {
        return l != j.l ? l < j.l : r < j.r;
    }
    bool operator==(const runs &j) {
        return l == j.l && r == j.r && p == j.p;
    }
} q[2000001];
inline void get_runs(int &k) {
    static int st[1000001];
    st[0] = n + 1;
    using has::a;

    for (int i = n, top = 0, lt = 0; i; i--) {
        while (top) {
            int x = std::min(st[top] - i, st[top - 1] - st[top]);
            lt = lcp(i, st[top], x);

            if ((lt == x && st[top] - i < st[top - 1] - st[top]) ||
                    (lt < x && a[i + lt] < a[st[top] + lt]))
                top--, lt = 0;
            else
                break;
        }

        int j = st[top];
        st[++top] = i;
        int x = lcs(i - 1, j - 1, n), y;

        if (x < j - i) {
            y = lcp(i, j, n);

            if (x + y >= j - i) {
                k++;
                q[k] = runs{i - x, j + y - 1, j - i};
            }
        }
    }
}

namespace SA {
    constexpr int $n = 800000;
    int rk_base[2][$n];
    int sa[$n + 1], *rk = rk_base[0], *rk2 = rk_base[1], bucket[$n + 1], sa2[$n + 1];
    int height[$n + 1], ST[$n + 1][21], STM;
    int mylog2[$n + 1];
    
    void get_sa(char *s, int n) {
        int i, m = 128, d, j, *tmp, t;
        for(i = 1; i <= m; ++i) bucket[i] = 0;
        for(i = 1; i <= n; ++i) bucket[rk[i] = s[i]] += 1;
        for(i = 2; i <= m; ++i) bucket[i] += bucket[i - 1];
        for(i = 1; i <= n; ++i) sa[bucket[rk[i]]--] = i;
        
        for(d = 1; d < n; d <<= 1) {
            for(i = 0; i < d; ++i) sa2[i] = n - i;
            for(i = 1, j = d; i <= n; ++i) if(sa[i] > d) sa2[j++] = sa[i] - d;
            
            for(i = 1; i <= m; ++i) bucket[i] = 0;
            for(i = 1; i <= n; ++i) bucket[rk[i]] += 1;
            for(i = 2; i <= m; ++i) bucket[i] += bucket[i - 1];
            for(i = n - 1; ~i; --i) sa[bucket[rk[sa2[i]]]--] = sa2[i];
            
            rk2[sa[1]] = 1;
            for(i = 2; i <= n; ++i) rk2[sa[i]] = rk2[sa[i - 1]] + \
                (rk[sa[i]] != rk[sa[i - 1]] || rk[sa[i] + d] != rk[sa[i - 1] + d]);
            
            tmp = rk, rk = rk2, rk2 = tmp;
            m = rk[sa[n]]; if(m == n) break;
        }
        
        for(i = 1, j = 0; i <= n; ++i) {
            if(j) j -= 1;
            while(s[i + j] == s[sa[rk[i] - 1] + j]) j += 1;
            height[rk[i]]  = j;
        }
        for(i = 1; i <= n; ++i) ST[i][0] = height[i];
        for(i = 1, t = 2; t < n; ++i, t <<= 1)
            for(j = 1; j + t <= n + 1; ++j)
                ST[j][i] = std::min(ST[j][i - 1], ST[j + (t >> 1)][i - 1]);
        STM = i - 1;
        mylog2[1] = 0;
        for(i = 2; i < n; ++i) mylog2[i] = mylog2[i >> 1] + 1;
        return ;
    }
    
    // make sure a != b
    int lcp(int a, int b) {
        // std::cout << "lcp(" << a << ", " << b << ") = ";
        a = rk[a], b = rk[b];
        if(a > b) std::swap(a, b);
        a += 1;
        int tmp = mylog2[b - a + 1];
        // std::cout << std::min(ST[a][tmp], ST[b - (1 << tmp) + 1][tmp]) << char(10);
        return std::min(ST[a][tmp], ST[b - (1 << tmp) + 1][tmp]);
    }
}

char hkr[400004];

int main() {
    scanf("%s", t + 1), m = strlen(t + 1);
    scanf("%s", s + 1), n = strlen(s + 1);

    for (int i = 1; i <= n; i++)
        has::a[i] = 25 - (s[i] - 'a');

    has::build(n), get_runs(k);

    for (int i = 1; i <= n; i++)
        has::a[i] = 25 - has::a[i];

    has::build(n), get_runs(k);
    std::sort(q + 1, q + k + 1), k = std::unique(q + 1, q + k + 1) - q - 1;
    
    int64_t ans = 0;
    constexpr int mod = 998244353;
    
    
    for(int i = 1; i <= n; ++i)
        hkr[i] = s[i];
    hkr[n + 1] = '?';
    for(int i = 1; i <= m; ++i)
        hkr[n + 1 + i] = t[i];
    hkr[m + n + 2] = 0;
    const int L = n + m + 1;
    
    SA::get_sa(hkr, L);
    
    std::vector<int> tt;
    for(int i = 1; i <= L; ++i) if(SA::sa[i] > n + 1) tt.push_back(SA::sa[i]);
    
    for(int i = 1; i <= k; ++i) {
        auto [L, R, P] = q[i];
        using SA::sa, SA::rk, SA::lcp;
        
        auto get_match = [&] (int sl, int sr) -> int64_t {
            std::vector<int>::iterator l, r, mid, al, ar;
            l = tt.begin(), r = tt.end();
            while(l < r) {
                mid = l + (r - l) / 2;
                if(rk[*mid] > rk[sl] || lcp(*mid, sl) >= sr - sl + 1)
                    r = mid;
                else l = mid + 1;
            }
            al = l;
            l = tt.begin(), r = tt.end();
            while(l < r) {
                mid = l + (r - l) / 2;
                if(rk[*mid] > rk[sl] && lcp(*mid, sl) < sr - sl + 1)
                    r = mid;
                else
                    l = mid + 1;
            }
            ar = l;
            // std::cout << "get_match(" << sl << ", " << sr << ") = " << ar - al << char(10);
            return ar - al;
        };
        
        for(int d = 0; d < P; ++d) {
            for(int h = 1; L + d + 2 * h * P - 1 <= R; ++h) {
                ans += get_match(L + d, L + d + h * P - 1) *
                    ( (R - (L + d + 2 * h * P - 1) + P ) / (P) ) % mod;
                // std::cout << ( (R - (L + d + 2 * h * P - 1) + P ) / (P) ) << char(10);
            }
        }
    }
    printf("%lld\n", ans % mod);
}

World Finals

签到题,我连题意都不知道

#include<bits/stdc++.h>
using namespace std;

const int INF = (int)1e9+5;

struct Node{
	string name;
	int p, t;
	bool operator<(const Node& b)const{return b.p!=p ? p>b.p : t<b.t;}	
};

const string myName = "lzr010506";
int n, ans;
set<string> st1, st2;

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	ans = INF;
	cin >> n;
	vector<Node> vec1;
	for (int i=1; i<=n; ++i){
		string str; int p, t;
		cin >> str >> p >> t;
		vec1.push_back(Node{str, p, t});
		st1.insert(str);
	}
	
	cin >> n;
	vector<Node> vec2;
	for (int i=1; i<=n; ++i){
		string str; int p, t;
		cin >> str >> p >> t;
		vec2.push_back(Node{str, p, t});
		if (st1.count(str)) st2.insert(str);	
	}
	
	int rk=1;
	sort(vec1.begin(), vec1.end());
	for (int i=0; i<(int)vec1.size(); ++i){
		auto [name, _, __] = vec1[i];
		if (name == myName){
			ans = min(ans, rk);
			break;
		}else if (!st2.count(name)){
			++rk;	
		}
	}
	rk=1;
	sort(vec2.begin(), vec2.end());
	for (int i=0; i<(int)vec2.size(); ++i){
		auto [name, _, __] = vec2[i];
		if (name == myName){
			ans = min(ans, rk);
			break;
		}else if (!st2.count(name)){
			++rk;	
		}
	}
	cout << ans << '\n';
	return 0;	
}

Mirror Maze

首先很套路地把一个格子拆成从四个方向对应的点,显然最后每个点至多有一条出边

显然根据光线可逆我们可以从终止状态(要么是光线跑到外面了,要么是在一个环上无限打转)倒推从每个状态出发的贡献

由于这个图的性质,环只有可能出现在终止状态,即我们把环缩了后得到的剩下的一定是个森林

那么可以直接在上面暴力 DFS 求出每个状态的贡献,而实现是我懒得写判圈了就写了个 Tarjan 缩点

当然后面思考了下发现其实由于这个图的性质缩完点后得到的一定是条链而不是森林,可以写的更简单点

#include<cstdio>
#include<iostream>
#include<vector>
#include<array>
#define RI int
#define CI const int&
using namespace std;
const int N=1005,S=N*N*4;
enum
{
	UP=0,
	LEFT,
	DOWN,
	RIGHT
};
int n,m,q,x,y,val[S],bkt[N*N],ans[S],ret;
char s[N][N],dirt[10]; vector <int> v[S],nv[S],num[S];
int dfn[S],low[S],idx,stk[S],top,bel[S],scc,deg[S]; bool vis[S];
inline void Tarjan(CI now)
{
	dfn[now]=low[now]=++idx; stk[++top]=now; vis[now]=1;
	for (auto to:v[now]) if (!dfn[to]) Tarjan(to),low[now]=min(low[now],low[to]);
	else if (vis[to]) low[now]=min(low[now],dfn[to]);
	if (dfn[now]==low[now])
	{
		bel[now]=++scc; vis[now]=0; num[scc].push_back(now);
		while (stk[top]!=now)
		bel[stk[top]]=scc,num[scc].push_back(stk[top]),vis[stk[top--]]=0;
		--top;
	}
}
inline void add(CI x)
{
	if (++bkt[x]==1) ++ret;
}
inline void del(CI x)
{
	if (--bkt[x]==0) --ret;
}
inline void DFS(CI now)
{
	for (auto x:num[now]) if (val[x]) add(x>>2);
	for (auto x:num[now]) ans[x]=ret;
	for (auto to:nv[now]) DFS(to);
	for (auto x:num[now]) if (val[x]) del(x>>2);
}
inline int ID(CI x,CI y,CI d)
{
	return 4*((x-1)*m+y)+d;
}
int main()
{
	RI i,j,k; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%s",s[i]+1);
	for (i=1;i<=n;++i) for (j=1;j<=m;++j) for (k=0;k<4;++k)
	{
		auto is_touch=[&](const char& mir,CI d)
		{
			if (mir=='/'||mir=='\\') return 1;
			if (mir=='-')
			{
				if (d==UP||d==DOWN) return 1;
				else return 0;
			} else
			{
				if (d==LEFT||d==RIGHT) return 1;
				else return 0;
			}
		};
		auto GO=[&](CI x,CI y,const char& mir,CI d) -> array <int,3>
		{
			if (mir=='-')
			{
				if (d==LEFT) return {x,y+1,LEFT};
				if (d==RIGHT) return {x,y-1,RIGHT};
				if (d==UP) return {x-1,y,DOWN};
				if (d==DOWN) return {x+1,y,UP};
			} else
			if (mir=='|')
			{
				if (d==LEFT) return {x,y-1,RIGHT};
				if (d==RIGHT) return {x,y+1,LEFT};
				if (d==UP) return {x+1,y,UP};
				if (d==DOWN) return {x-1,y,DOWN};
			} else
			if (mir=='/')
			{
				if (d==LEFT) return {x-1,y,DOWN};
				if (d==RIGHT) return {x+1,y,UP};
				if (d==UP) return {x,y-1,RIGHT};
				if (d==DOWN) return {x,y+1,LEFT};
			} else
			{
				if (d==LEFT) return {x+1,y,UP};
				if (d==RIGHT) return {x-1,y,DOWN};
				if (d==UP) return {x,y+1,LEFT};
				if (d==DOWN) return {x,y-1,RIGHT};
			}
            return {0,0,0};
		};
		val[ID(i,j,k)]=is_touch(s[i][j],k);
		auto [x,y,d]=GO(i,j,s[i][j],k);
		if (x<1||x>n||y<1||y>m) continue;
		else v[ID(x,y,d)].push_back(ID(i,j,k));
	}
	int st=ID(1,1,0),ed=ID(n,m,3);
	for (i=st;i<=ed;++i) if (!dfn[i]) Tarjan(i);
	for (i=st;i<=ed;++i) for (auto j:v[i])
	if (bel[i]!=bel[j]) nv[bel[i]].push_back(bel[j]),++deg[bel[j]];
	for (i=1;i<=scc;++i) if (!deg[i])
	{
		for (auto x:num[i]) if (val[x]) add(x>>2);
		for (auto x:num[i]) ans[x]=ret;
		for (auto to:nv[i]) DFS(to);
		for (auto x:num[i]) if (val[x]) del(x>>2);
	}
	for (scanf("%d",&q);q;--q)
	{
		scanf("%d%d%s",&x,&y,dirt); int d;
		if (dirt[0]=='a') --x,d=DOWN; else
		if (dirt[0]=='b') ++x,d=UP; else
		if (dirt[0]=='l') --y,d=RIGHT; else ++y,d=LEFT;
		if (x<1||x>n||y<1||y>m) { puts("0"); continue; }
		printf("%d\n",ans[ID(x,y,d)]);
	}
	return 0;
}

2D Travel

首先可以发现横纵两维是独立的,可以分别处理,因此现在只用考虑一维的问题

不难发现如果从初始状态开始一直不碰到边界的话就是个很trivial的题

求出前缀和数组后想到与要找到从某个位置开始的前缀最大/最小值,可以用二分+ST表做到 \(O(\log k)\) 查询

而碰到边界的话不难发现此时开始转移就不和初始位置有关了,只要对每个操作维护出其下一次撞墙走到的操作以及走过的距离,直接倍增处理一下即可 \(O(\log k)\) 查询

综上,总复杂度 \(O((k+q)\log k)\),实现起来可能比较复杂需要一点耐心

#include<bits/stdc++.h>
using namespace std;
#define int long long

using pii = pair<int, int>;
array<int, 2> dir[128];

const int INF = (int)1e9+5;
const int N = 1e5+5;
const int B = 20;
int k, q;
array<int, 2> n;
array<int, 2> op[N];
int to[N][B], wt[N][B];
int sum[N], len[N];
struct Qry{
	array<int, 2> pos;
	int L, R;
	array<int, 2> apos;
	int ans;	
}qry[N];

int lg[N];
int mx[N][B], mn[N][B];
void init(){
	for (int i=1; i<=k+1; ++i) mx[i][0]=mn[i][0]=sum[i];
	for (int j=1; j<B; ++j){
		for (int i=1; i+(1<<j)-1<=k; ++i){
			mx[i][j] = max(mx[i][j-1], mx[i+(1<<(j-1))][j-1]);
			mn[i][j] = min(mn[i][j-1], mn[i+(1<<(j-1))][j-1]);	
		}
	}
}
int getval(int L, int R, int sg){
//	printf("getval\n");
	if (R<L) return 0;
	int len = R-L+1;
	if (sg>0) return max(mx[L][lg[len]], mx[R-(1<<lg[len])+1][lg[len]]);
	return min(mn[L][lg[len]], mn[R-(1<<lg[len])+1][lg[len]]);
}
int finddd(int pos, int val, int sg){
//	printf("find\n");
	int L=pos+1, R=k+1;
	while (L<R){
		int M = L+(R-L)/2;
		int res = getval(L, M, sg);
		if (sg*res > sg*val) R=M;
		else L=M+1;	
	}
	return L;
}	

signed main(){
	ios::sync_with_stdio(0); cin.tie(0);
	dir['L']={-1, 0}; dir['R']={1, 0}; dir['U']={0, 1}; dir['D']={0, -1};
	cin >> n[0] >> n[1] >> k >> q;
	for (int i=1; i<=k; ++i){
		char d; int x;
		cin >> d >> x;
		op[i][0] = dir[d][0]*x;
		op[i][1] = dir[d][1]*x;
	}
	op[k+1][0]=op[k+1][1]=INF;
	
	for (int i=1; i<=q; ++i){
		int x, y, L, R; cin >> x >> y >> L >> R;
		qry[i] = Qry{{x, y}, L, R};
	}
	
	lg[0]=lg[1]=0;
	for (int i=2; i<N; ++i) lg[i]=lg[i>>1]+1;
	
	for (int h=0; h<2; ++h){
		for (int i=1; i<=k+1; ++i) sum[i]=sum[i-1]+op[i][h];
		for (int i=1; i<=k+1; ++i) len[i]=len[i-1]+abs(op[i][h]);
//		printf("sum:"); for (int i=1; i<=k+1; ++i) printf("%lld ", sum[i]); puts("");
		init();
		
		for (int i=1; i<=k; ++i){
			if (op[i][h]==0){
				to[i][0]=k+1, wt[i][0]=0;
				continue;	
			}
			int stp = (op[i][h] > 0 ? n[h] : 0) - sum[i];
			int mxbd = n[h]-stp, mnbd = -stp;
//			printf("i=%lld mxbd=%lld mnbd=%lld\n", i, mxbd, mnbd);
			int Rv = finddd(i, mxbd, 1), Lv = finddd(i, mnbd, -1);
			int v = min(Rv, Lv);
			to[i][0] = v;
			if (Rv<Lv) wt[i][0] = len[v]-len[i] + (n[h]-(stp+sum[v]));
			else wt[i][0] = len[v]-len[i] + (stp+sum[v]);
		}
		
//		printf("k=%lld\n", k);
//		
		wt[k+1][0]=0, to[k+1][0]=k+1;
		for (int j=1; j<B; ++j){
			for (int i=1; i<=k+1; ++i){
				to[i][j] = to[to[i][j-1]][j-1];
				wt[i][j] = wt[i][j-1] + wt[to[i][j-1]][j-1];
			}
		}
		
//		for (int j=0; j<1; ++j){
//			for (int i=1; i<=k+1; ++i){
////				printf("i=%lld to=%lld wt=%lld\n", i, to[i][j], wt[i][j]);
//	//			printf("i=%lld\n", i);
//			}
//		}
		for (int i=1; i<=q; ++i){
			int stp = qry[i].pos[h] - sum[qry[i].L-1];
			int mxbd = n[h]-stp, mnbd = -stp;
			int Rv = finddd(qry[i].L-1, mxbd, 1), Lv = finddd(qry[i].L-1, mnbd, -1), v;
			v = min(Rv, Lv);
//			printf("mxbd=%lld mnbd=%lld\n", mxbd, mnbd);
//			printf("i=%lld v=%lld Rv=%lld Lv=%lld\n", i, v, Rv, Lv);
			
//			printf("qry[i].R=%lld\n", qry[i].R);
			if (v>qry[i].R){
				qry[i].ans += len[qry[i].R]-len[qry[i].L-1];
				qry[i].apos[h] = qry[i].pos[h] + sum[qry[i].R]-sum[qry[i].L-1];
//				printf("111\n");
				continue;	
			}
			
			if (Rv<Lv) qry[i].ans += len[v]-len[qry[i].L-1] + (n[h]-(stp+sum[v]));
			else qry[i].ans += len[v]-len[qry[i].L-1] + (stp+sum[v]);
			if (op[v][h]>0) qry[i].apos[h] = n[h];
			else qry[i].apos[h] = 0; 
//			printf("qry[%lld].ans=%lld\n", i, qry[i].ans);
			for (int j=B-1; j>=0; --j){
				if (to[v][j] <= qry[i].R){
//					printf("v=%lld to=%lld\n", v, to[v][j]);
					qry[i].ans += wt[v][j];	
					v=to[v][j];
					if (op[v][h]>0) qry[i].apos[h] = n[h];
					else qry[i].apos[h] = 0; 
				}
			}
			qry[i].ans += len[qry[i].R]-len[v];
			qry[i].apos[h] += sum[qry[i].R]-sum[v];
		}
	}
	
	for (int i=1; i<=q; ++i){
		cout << qry[i].apos[0] << ' ' << qry[i].apos[1] << ' ' << qry[i].ans << '\n';	
	}
	return 0;	
}

Matching Problem

看题解 PPT 的页数可知这应该是本场最难的题了,弃疗


Postscript

感觉今天很抽象啊,过的人多的 B,D 不会,反而在开过的很少的 J,G,导致被打爆了

总结是因为我又在梦游,鉴定为纯纯的飞舞

posted @ 2024-07-16 21:02  空気力学の詩  阅读(680)  评论(0编辑  收藏  举报