A 牛牛的mex运算

一.题目大意

给出 个数 次询问,每次给出 并询问 .

互异.

二.分析

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

根据题目条件不难得 的一个排列.

因此 .

三.代码实现

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 牛牛的算术

一.题目大意

T组询问,每组询问给出 ,求

二.分析

化简可得 .

很容易想到当 大于某个值时,答案恒为 0. (打表可得当 时,答案为 0.)

那么直接搞就完事惹.

三.代码实现

#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 牛牛的无向图

一.题目大意

给出 个点 条边的无向图,每条边有权值 .

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

定义 的所有路径中权值最小的路径的权值.

次询问,每次询问给出 ,求

次答案异或一遍并输出.

.

二.分析

很容易想到对 排序,这样可以保证答案不降.

因此,对于处在 的边 ,我们只需要统计有多个新点对产生.

不难(用)证明,按照 算法步骤, 一定是连通块 所有点 与连通块 所有点 .

详见代码.

三.代码实现

#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 牛牛的粉丝

一.题目大意

有一个环,长度为 ,环上每个位置上有 个人.

先进行 次活动,每次活动每个人有 的概率按顺时针方向走一个单位, 的概率按逆时针方向走一个单位, 的概率待在原地.

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

.

二.分析

为第 次活动结束后, 号位置人数的期望.

易得 .

由于 最大是 ,我们用矩阵快速幂优化递推式. 即:

可这样时间复杂度是 ,妥妥的 TLE...

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

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

2.循环矩阵某一行或某一***定,则循环矩阵确定.

根据上述两条性质,我们则可把时间复杂度优化到 .

三.代码实现

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