牛客练习赛68 A B C D

A 牛牛的mex运算

一.题目大意

\quad 给出 n n n 个数 a [ 1.. n ] a[1..n] a[1..n] q q q 次询问,每次给出 l , r l, r l,r并询问 m e x ( a [ l . . r ] ) mex(a[l..r]) mex(a[l..r]).

\quad n , q ≤ 1 0 5 , 0 ≤ a ≤ n − 1 n,q \leq 10^5, 0 \leq a \leq n - 1 n,q105,0an1 a [ i ] a[i] a[i] 互异.

二.分析

\quad 赛时用的莫队,看题解才发现自己写麻烦了.

\quad 根据题目条件不难得 a [ ] a[] a[] 0.. n − 1 0..n-1 0..n1 的一个排列.

\quad 因此 m e x ( a [ l . . r ] ) = m i n ( a [ 1.. l − 1 ] , a [ r + 1.. n ] ) mex(a[l..r])=min(a[1..l-1], a[r+1..n]) mex(a[l..r])=min(a[1..l1],a[r+1..n]).

三.代码实现

1. 莫队

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

typedef unsigned long long ull;
typedef long long ll;

const int M = (int)1e5;
const int N = (int)5e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;
const double eps = 1e-3;

int n, a[M + 5];
int q, block;

struct node
{
    
    
    int l, r, id, bl;

    bool operator< (const node& b)const
    {
    
    
        if(bl != b.bl) return bl < b.bl;
        return r < b.r;
    }
}s[M + 5];

int cur, ans[M + 5];
int cnt[M + 5];

void add(int x)
{
    
    
    ++cnt[a[x]];
    while(cnt[cur]) ++cur;
}

void sub(int x)
{
    
    
    --cnt[a[x]];
    if(!cnt[a[x]]) cur = min(cur, a[x]);
}

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    scanf("%d %d", &n, &q); block = (int)sqrt(n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for(int i = 1; i <= q; ++i) scanf("%d %d", &s[i].l, &s[i].r), s[i].id = i, s[i].bl = s[i].l / block;
    sort(s + 1, s + q + 1);
    int l = 1, r = 0;
    for(int i = 1; i <= q; ++i)
    {
    
    
        while(r < s[i].r) ++r, add(r);
        while(r > s[i].r) sub(r), --r;
        while(l < s[i].l) sub(l), ++l;
        while(l > s[i].l) --l, add(l);
        ans[s[i].id] = cur;
    }
    for(int i = 1; i <= q; ++i) printf("%d\n", ans[i]);
    return 0;
}

2. 简单操作

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

typedef unsigned long long ull;
typedef long long ll;

const int M = (int)1e5;
const int N = (int)5e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;
const double eps = 1e-3;

int n, q, a[M + 5];
int pre[M + 5], suf[M + 5];

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    scanf("%d %d", &n, &q);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    pre[0] = n; for(int i = 1; i <= n; ++i) pre[i] = min(pre[i - 1], a[i]);
    suf[n + 1] = n; for(int i = n; i >= 1; --i) suf[i] = min(suf[i + 1], a[i]);
    int l, r;
    while(q--)
    {
    
    
        scanf("%d %d", &l, &r);
        printf("%d\n", min(pre[l - 1], suf[r + 1]));
    }
    return 0;
}

B 牛牛的算术

一.题目大意

\quad T组询问,每组询问给出 n n n,求 ∏ i = 1 n ∑ j = 1 i ∑ k = 1 j i j k ( m o d    199999 ) \begin{aligned} \prod_{i=1}^{n} \sum_{j=1}^i \sum_{k=1}^j ijk (mod \; 199999)\end{aligned} i=1nj=1ik=1jijk(mod199999)

\quad ∑ n ≤ 1 0 1 0 5 \begin{aligned} \sum n \leq 10^{10^5} \end{aligned} n10105

二.分析

\quad 化简可得 ∏ i = 1 n ∑ j = 1 i ∑ k = 1 j i j k = ∏ i = 1 n [ 1 8 i 5 + 5 12 i 4 + 3 8 i 3 + 1 12 i 2 ] \begin{aligned} \prod_{i=1}^{n} \sum_{j=1}^i \sum_{k=1}^j ijk = \prod_{i=1}^n [\frac{1}{8}i^5 + \frac{5}{12}i^4 + \frac{3}{8}i^3 + \frac{1}{12}i^2]\end{aligned} i=1nj=1ik=1jijk=i=1n[81i5+125i4+83i3+121i2].

\quad 很容易想到当 n n n 大于某个值时,答案恒为 0. (打表可得当 n ≥ 66666 n \geq 66666 n66666 时,答案为 0.)

\quad 那么直接搞就完事惹.

三.代码实现

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

typedef unsigned long long ull;
typedef long long ll;

const int M = (int)1e5;
const int N = (int)1e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)199999;
const double eps = 1e-3;

ll quick(ll a, ll b, ll p = mod)
{
    
    
    ll s = 1;
    while(b)
    {
    
    
        if(b & 1) s = s * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return s;
}

ll inv(ll n, ll p = mod)
{
    
    
    return quick(n, p - 2, p);
}

ll cal(int n)
{
    
    
    ll s = 0;
    s += inv(8) % mod * n % mod * n % mod * n % mod * n % mod * n % mod; s %= mod;
    s += 5 % mod * inv(12) % mod * n % mod * n % mod * n % mod * n % mod; s %= mod;
    s += 3 % mod * inv(8) % mod * n % mod * n % mod * n % mod; s %= mod;
    s += inv(12) % mod * n % mod * n % mod; s %= mod;
    return s;
}

char s[M + 5];

ll f[M + 5];

void init()
{
    
    
    f[0] = 1;
    for(int i = 1; i <= 100000; ++i)
    {
    
    
        f[i] = f[i - 1] * cal(i) % mod;
    }
    f[0] = 0;
}

void work()
{
    
    
    scanf("%s", s + 1);
    int len = strlen(s + 1);
    if(len >= 6)
    {
    
    
        printf("0\n");
        return;
    }
    int n = 0;
    for(int i = 1; i <= len; ++i) n = n * 10 + s[i] - '0';
    printf("%lld\n", f[n]);
}

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    init();
    int T; scanf("%d", &T);
    while(T--) work();
    return 0;
}

C 牛牛的无向图

一.题目大意

\quad 给出 n n n 个点 m m m 条边的无向图,每条边有权值 w [ i ] w[i] w[i].

\quad 定义一条路径的权值为这条路径上边权最大的边的权值.

\quad 定义 d ( u , v ) d(u, v) d(u,v) u u u v v v 的所有路径中权值最小的路径的权值.

\quad q q q 次询问,每次询问给出 L L L,求 ∑ 1 ≤ u < v ≤ n [ d ( u , v ) ≤ L ] \begin{aligned} \sum_{1\leq u < v \leq n}[d(u, v) \leq L] \end{aligned} 1u<vn[d(u,v)L]

\quad q q q 次答案异或一遍并输出.

\quad 1 ≤ n ≤ 1 0 5 , 1 ≤ m , q ≤ 5 × 1 0 5 , 1 ≤ w , L ≤ 1 0 9 1 \leq n \leq 10^5, 1 \leq m,q \leq 5\times 10^5, 1 \leq w, L \leq 10^9 1n105,1m,q5×105,1w,L109.

二.分析

\quad 很容易想到对 w [ i ] w[i] w[i] L [ i ] L[i] L[i] 排序,这样可以保证答案不降.

\quad 因此,对于处在 [ L [ i − 1 ] + 1 , L [ i ] ] [L[i-1] + 1 , L[i]] [L[i1]+1,L[i]] 的边 e e e,我们只需要统计有多个新点对产生.

\quad 不难(用)证明,按照 K r u s k a l Kruskal Kruskal 算法步骤, e e e 一定是连通块 u u u 所有点 u ′ u' u与连通块 v v v 所有点 v ′ v' v d ( u ′ , v ′ ) d(u', v') d(u,v).

\quad 详见代码.

三.代码实现

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

typedef unsigned long long ull;
typedef long long ll;

const int M = (int)1e6;
const int N = (int)5e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;
const double eps = 1e-3;

struct node
{
    
    
    int cat;
    int u, v, w;

    bool operator< (const node& b)const
    {
    
    
        if(w != b.w) return w < b.w;
        return cat < b.cat;
    }
}s[M + 5];

unsigned int SA, SB, SC; int n, m, q, LIM;
unsigned int rng61(){
    
    
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}

void gen(){
    
    
    scanf("%d%d%d%u%u%u%d", &n, &m, &q, &SA, &SB, &SC, &LIM);
    for(int i = 1; i <= m; i++){
    
    
        s[i].u = rng61() % n + 1;
        s[i].v = rng61() % n + 1;
        s[i].w = rng61() % LIM;
        s[i].cat = 1;
    }
    for(int i = 1; i <= q; i++){
    
    
        s[i + m].w = rng61() % LIM;
        s[i + m].cat = 2;
    }
}

int fa[M + 5], sz[M + 5];

int tofind(int x)
{
    
    
    if(x == fa[x]) return x;
    return fa[x] = tofind(fa[x]);
}

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    gen();
    sort(s + 1, s + m + q + 1);
    for(int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
    ll ans = 0, cur = 0;
    for(int i = 1, u, v; i <= m + q; ++i)
    {
    
    
        if(s[i].cat == 1)
        {
    
    
            u = s[i].u, v = s[i].v;
            u = tofind(u), v = tofind(v);
            if(u == v) continue;
            cur += sz[u] * sz[v];
            fa[u] = v, sz[v] += sz[u];
        }
        else ans ^= cur;
    }
    printf("%lld\n", ans);
    return 0;
}

D 牛牛的粉丝

一.题目大意

\quad 有一个环,长度为 n n n,环上每个位置上有 x [ i ] x[i] x[i] 个人.

\quad 先进行 k k k 次活动,每次活动每个人有 p 1 p_1 p1 的概率按顺时针方向走一个单位, p 2 p_2 p2 的概率按逆时针方向走一个单位, p 3 p_3 p3 的概率待在原地.

\quad k k k 次活动后,每个位置上人数的期望.

\quad 3 ≤ n ≤ 500 , 0 ≤ k ≤ 1 0 18 , x [ i ] ≤ 1 0 6 3 \leq n \leq 500, 0 \leq k \leq 10^{18},x[i] \leq 10^6 3n500,0k1018x[i]106.

二.分析

\quad f [ i , j ] f[i, j] f[i,j] 为第 i i i 次活动结束后, j j j 号位置人数的期望.

\quad 易得 f [ i , j ] = p 1 f [ i − 1 , j − 1 ] + p 2 f [ i − 1 , j + 1 ] + p 3 f [ i − 1 , j ] f[i, j] = p_1f[i - 1, j - 1] + p_2f[i - 1, j + 1] + p_3f[i - 1, j] f[i,j]=p1f[i1,j1]+p2f[i1,j+1]+p3f[i1,j].

\quad 由于 k k k 最大是 1 0 18 10^{18} 1018,我们用矩阵快速幂优化递推式. 即:

\quad [ f [ i , 0 ] . . . f [ i , n − 1 ] ] = [ f [ i − 1 , 0 ] . . . f [ i − 1 , n − 1 ] ] × [ p 3 p 1 0 . . . . . . . . . 0 p 2 p 2 p 3 p 1 0 . . . . . . . . . 0 0 p 2 p 3 p 1 0 . . . . . . 0 0 0 p 2 p 3 p 1 0 . . . 0 . . . . . . . . p 1 0 . . . . . . . . . 0 p 2 p 3 ] \left[ \begin{matrix} f[i, 0] & ... & f[i, n - 1] \end{matrix} \right] = \left[ \begin{matrix} f[i-1, 0] & ... & f[i-1, n - 1] \end{matrix} \right] \times \left[ \begin{matrix} p_3 & p_1 & 0 & ... & ... & ... & 0 & p_2 \\ p_2 & p_3 & p_1 & 0 & ... & ... & ... & 0 \\ 0 & p_2 & p_3 & p_1 & 0 & ... & ... & 0 \\ 0 & 0 & p_2 & p_3 & p_1 & 0 & ... & 0 \\ . & . & . & . & . & . & . & . \\ p_1 & 0 & ... & ... & ...& 0 & p_2 & p_3 \end{matrix} \right] [f[i,0]...f[i,n1]]=[f[i1,0]...f[i1,n1]]×p3p200.p1p1p3p20.00p1p3p2.......0p1p3..........0p1.............0.00..........p2p2000.p3

\quad 可这样时间复杂度是 O ( n 3 l o g k ) O(n^3logk) O(n3logk),妥妥的 TLE…

\quad 观察到系数矩阵是循环矩阵,而循环矩阵有两个特点:

\quad \quad 1.循环矩阵对加法和乘法封闭.

\quad \quad 2.循环矩阵某一行或某一列确定,则循环矩阵确定.

\quad 根据上述两条性质,我们则可把时间复杂度优化到 O ( n 2 l o g k ) O(n^2logk) O(n2logk).

三.代码实现

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

typedef unsigned long long ull;
typedef long long ll;

const int M = (int)5e2;
const int N = (int)5e5;
const int inf = 0x3f3f3f3f;
const ll mod = (ll)998244353;
const double eps = 1e-3;

int n, a, b, c, s; ll k, p1, p2, p3;

struct Matrix
{
    
    
    ll D[M + 5][M + 5];
}A, B, S, C;

ll quick(ll a, ll b, ll p = mod)
{
    
    
    ll s = 1;
    while(b)
    {
    
    
        if(b & 1) s = s * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return s;
}

ll inv(ll n, ll p = mod)
{
    
    
    return quick(n, p - 2, p);
}

void work()
{
    
    
    S = B; --k;
    while(k)
    {
    
    
        if(k & 1)
        {
    
    
            for(int i = 0; i < n; ++i) C.D[i][0] = 0;
            for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) C.D[i][0] = (C.D[i][0] + S.D[i][j] * B.D[j][0] % mod) % mod;
            for(int i = 0; i < n; ++i)
            {
    
    
                for(int j = 0; j < n; ++j)
                {
    
    
                    C.D[j][(j - i + n) % n] = C.D[i][0];
                }
            }
            memcpy(S.D, C.D, sizeof(C.D));
        }
        {
    
    
            for(int i = 0; i < n; ++i) C.D[i][0] = 0;
            for(int i = 0; i < n; ++i) for(int j = 0; j < n; ++j) C.D[i][0] = (C.D[i][0] + B.D[i][j] * B.D[j][0] % mod) % mod;
            for(int i = 0; i < n; ++i)
            {
    
    
                for(int j = 0; j < n; ++j)
                {
    
    
                    C.D[j][(j - i + n) % n] = C.D[i][0];
                }
            }
            memcpy(B.D, C.D, sizeof(C.D));
        }
        k >>= 1;
    }
    for(int i = 0; i < n; ++i) C.D[0][i] = 0;
    for(int i = 0; i < n; ++i)
    {
    
    
        for(int j = 0; j < n; ++j)
        {
    
    
            C.D[0][i] = (C.D[0][i] + A.D[0][j] * S.D[j][i]) % mod;
        }
    }
}

int main()
{
    
    
//    freopen("input.txt", "r", stdin);
//    freopen("output.txt", "w", stdout);
    scanf("%d %lld", &n, &k);
    scanf("%d %d %d", &a, &b, &c);
    s = a + b + c;
    p1 = a * inv(s) % mod, p2 = b * inv(s) % mod, p3 = c * inv(s) % mod;
    memset(A.D, 0, sizeof(A.D));
    memset(B.D, 0, sizeof(B.D));
    for(int i = 0; i < n; ++i) scanf("%lld", &A.D[0][i]);
    if(k == 0)
    {
    
    
        for(int i = 0; i < n; ++i) printf("%lld%c", A.D[0][i], i == n - 1 ? '\n' : ' ');
        return 0;
    }
    for(int i = 0; i < n; ++i)
    {
    
    
        B.D[(i - 1 + n) % n][i] = p1;
        B.D[(i + 1) % n][i] = p2;
        B.D[i][i] = p3;
    }
    work();
    for(int i = 0; i < n; ++i) printf("%lld%c", C.D[0][i], i == n - 1 ? '\n' : ' ');
    return 0;
}

猜你喜欢

转载自blog.csdn.net/The___Flash/article/details/108296856