开坑难填之A层邀请赛1

A. Race

据说很容易想到Trie树?但我当时只想到了暴力……(原因是Trie树还不会qwq)

//我相信我没分~
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int maxn = 2e5 + 3;
const ll mod = 998244353;

int m, n, a[maxn];
ll r[maxn], ans;
struct node 
{
    int id;
    ll val;
    bool operator < (const node &T) const 
    {
        return T.val < val;
    }
}c[maxn];

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    n = read(); m = read();
    for(int i=1; i<=n; i++) a[i] = read();

    int Day = (1<<m);
    for(int j=0; j<Day; j++)
    {
        //printf("j = %d\n", j);
        for(int i=1; i<=n; i++)
        {
            c[i].val = (a[i]^j);
            c[i].id = i;
            //printf("c[%d].val = %lld\n", i, c[i].val);
        }
        sort(c+1, c+1+n);
        /*for(int i=1; i<=n; i++)
        {
            printf("%lld\n", c[i].val);
        }*/
        for(int i=1; i<=n; i++)
        {
            int id = c[i].id;
            r[id] = (r[id]+(ll)(i-1)*(i-1))%mod;
        }
    }
    for(int i=1; i<=n; i++)
    {
        ans ^= r[i];
        //printf("r[%d] = %lld\n", i, r[i]);
        //printf("ans = %lld\n", ans);
    }
    printf("%lld", ans);
    
    return 0;
}
TLE 10

正解:先把x^2拆开变成(x1+x2+x3+...)*(x1+x2+x3+...)其中每一个xi都为1,代表当天更大的数,再继续展开就变成了(x1^2+x2^2+...+2*x1*x2+2*x1*x3+...),所以答案就和在它之前的点对的个数有了关系。

对每一个a[i],枚举在a[i]之前的点对,其中一个是从M-1到j-1异或值和a[i]相同,另一个是从M-1到k-1和a[i]相同,它们分别在第j位和第k位比a[i]的对应位大,find是找到前面的对应位相同,ch^1是当前位和a[i]相反,而单独找到相反还不够可能异或更小了,这就给天数设置了限制条件,一定可以找到满足条件的日子因为它所有位都是满的。j==k的情况是1<<(M-1)因为对M的限制只有一位。

这个应该不算是按位计算贡献吧,只是把位拆开来枚举点对而已……不过这么说好像也可以?

找点对的时候避免重复把k循环到j-1而不是把k==j continue掉。点对当然可以合并(在计算贡献时用树上的size),因为点对本来就是拆开的,拆成几个都一样。

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 5e5 + 5;
const int N = 1e7 + 2;
const int mod = 1e9 + 7;
const int INF = 1061109567;

int n, M, tot=1, fa[maxn][31];
ll ans[maxn], sum, a[maxn];
struct node 
{
    ll ch[2], size;
}t[maxn*20];
  
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

void insert(int x)
{
    int p = 1; t[p].size++; fa[x][M] = 1;
    for(int i=M-1; i>=0; i--)
    {
        int me = (a[x]>>i)&1;
        if(!t[p].ch[me]) t[p].ch[me] = ++tot;
        p = t[p].ch[me];
        t[p].size++;
        fa[x][i] = p;
    }
}

void work(int x)
{
    for(int i=0; i<=M-1; i++)
    {
        int me1 = 0, find1 = 0, me2 = 0, find2 = 0;
        for(int j=0; j<i; j++)
        {
            me1 = (a[x]>>i)&1; me2 = (a[x]>>j)&1;
            me1^=1; me2^=1;
            find1 = fa[x][i+1]; find2 = fa[x][j+1];
            ans[x] = (ans[x]+2*t[t[find1].ch[me1]].size*t[t[find2].ch[me2]].size%mod*(1ll<<(M-2ll))%mod)%mod;
        }
        me1 = (a[x]>>i)&1; me1^=1;
        find1 = fa[x][i+1];
        ans[x] = (ans[x]+t[t[find1].ch[me1]].size*t[t[find1].ch[me1]].size%mod*(1ll<<(M-1ll))%mod)%mod;
    }
}

int main()
{
    n = read(); M = read();
    for(int i=1; i<=n; i++)
    {
        a[i] = read();
    }
    for(int i=1; i<=n; i++)
    {
        insert(i);
    }  
    for(int i=1; i<=n; i++)
    {
        work(i);
    }
    for(int i=1; i<=n; i++)
    {
        sum ^= ans[i];
    }
    printf("%lld\n", sum);
  
    return 0;
}
View Code

 

B. 青蛙

赛时正解好像我也想到了,不会数cnt+1……WA 0……

如果这些石头连一只青蛙不付代价地跳过去都不够,那就让代价最小的青蛙把所有石头跳一遍,其他青蛙直接从头到尾;如果这些石头可以让至少一只青蛙不付代价地跳过去,那就尽可能让代价大的去跳石头,每次卡着上限去跳给其他青蛙也留足机会,不用考虑石头有剩余,直接分配给能跳过去的某只青蛙就好了,距离只会减得更小,一次跳不过去的青蛙直接从头到尾。

那种卡上限跳的思路可以用set实现,虽然有log不过据说可以过,但当时这么想纯属瞎蒙,并不会证明,于是我去借鉴了另一种跳法——%%%Chen_jr%%%妙不可言

讲题那天好像就是这么讲的,蒟蒻直到看见代码才知道说的是什么意思……

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 5e5 + 5;
const int N = 1e7 + 2;
const int mod = 1e9 + 7;
const int INF = 1061109567;

int T, n, m, k, d, c[maxn], a[maxn];
bool f[maxn];
  
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    T = read();
    while(T--)
    {
        n = read(); m = read(); k = read(); d = read();
        for(int i=1; i<=m; i++)
        {
            c[i] = read();
        }
        for(int i=1; i<=k; i++)
        {
            a[i] = read();
        }
        sort(a+1, a+k+1); sort(c+1, c+m+1);
        int p = 0, cnt = 0;
        for(int i=0; i<=k; i++) f[i] = 0;//memset
        for(; p<=k; p++)
        {
            if(a[p]-1 <= d) f[p] = 1;//表示有青蛙跳到了第p个石头上
            else break;
        }
        for(int i=1; i<=k; i++)
        {
            //为什么不判断f[p]不能是1,因为p跳完就往后增加,当然不可能是1
            if(f[i] && a[p]-a[i]<=d && p <= k)//从有青蛙的石头开始向后转移,最靠左的青蛙尝试最近的空石头
            {
                f[p] = 1; f[i] = 0; p++;//青蛙跳走了记得清空
            }
        }
        for(int i=k; i>=1; i--)//然后还要能到终点才行
        {
            if(n-a[i] <= d && f[i]) cnt++;
            else break;
        }
        ll ans = 0;
        if(cnt == 0)
        {
            a[0] = 1; a[k+1] = n;
            for(int i=1; i<=k+1; i++)
            {
                if(a[i]-a[i-1] > d) ans += c[1];
            }
            ans -= c[1];//因为后面没有else,c[1]又加了一遍
        }
        for(int i=1; i<=m-cnt; i++)
        {
            ans += c[i];
        }
        printf("%lld\n", ans);
    }
  
    return 0;
}
View Code

 

C. 序列

首先,暴力可以水个25分。然后,吉司机线段树是什么鬼啊,容我先去学习一下……

学习了一波,原来就是一种可以维护区间次小值的线段树,还可以记录区间最小值被修改的次数,单点查询的话直接就是答案了。算是收藏一种板子,目前还不知道怎么应用……

#include <bits/stdc++.h>
  
using namespace std;
  
typedef long long ll;
const int maxn = 1e5 + 5;
const int N = 1e7 + 2;
const int mod = 1e9 + 7;
const ll INF = 0x3f3f3f3f3f3f3f3f;

int n, m, val[maxn];
char c[3];
  
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-')
        {
            f = -1;
        }
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        x = (x << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

struct node 
{
    ll val; int op;
}ans;

struct tree 
{
    struct node 
    {
        ll dt, mi, cmi;
        int ctmi, ctdt;
    }t[maxn<<2];

    void pushup(int x)
    {
        int ls = x<<1, rs = x<<1|1;
        if(t[ls].mi < t[rs].mi)
        {
            t[x].mi = t[ls].mi;
            t[x].cmi = t[ls].cmi;
            t[x].cmi = min(t[x].cmi, t[rs].mi==t[x].mi?INF:t[rs].mi);
            t[x].cmi = min(t[x].cmi, t[rs].cmi==t[x].mi?INF:t[rs].cmi);
        }
        else 
        {
            t[x].mi = t[rs].mi;
            t[x].cmi = t[rs].cmi;
            t[x].cmi = min(t[x].cmi, t[ls].mi==t[x].mi?INF:t[ls].mi);
            t[x].cmi = min(t[x].cmi, t[ls].cmi==t[x].mi?INF:t[ls].cmi);
        }
    }

    void build(int x, int l, int r)
    {
        if(l == r)
        {
            t[x].mi = val[l]; t[x].cmi = INF;
            return;
        }
        int mid = (l + r) >> 1;
        build(x<<1, l, mid);
        build(x<<1|1, mid+1, r);
        pushup(x);
    }

    void pushdown(int x)
    {
        int ls = x<<1, rs = x<<1|1;
        if(t[x].ctdt)
        {
            t[ls].ctdt += t[x].ctdt;
            t[rs].ctdt += t[x].ctdt;
            t[ls].dt += t[x].dt;
            t[rs].dt += t[x].dt;
            //这个可以直接加是因为dt不是区间和,而且最后只有单点查询
            t[ls].mi += t[x].dt;
            t[rs].mi += t[x].dt;
            t[ls].cmi += t[x].dt;
            t[rs].cmi += t[x].dt;
            t[x].dt = 0;
            t[x].ctdt = 0;
        }
        if(t[x].ctmi)
        {
            if(t[ls].mi < t[x].mi)
            {
                t[ls].ctmi += t[x].ctmi;
                t[ls].mi = t[x].mi;
            }
            if(t[rs].mi < t[x].mi)
            {
                t[rs].ctmi += t[x].ctmi;
                t[rs].mi = t[x].mi;
            }
            t[x].ctmi = 0;
        }
    }

    void modify_add(int x, int l, int r, int L, int R, int dt)
    {
        if(L <= l && r <= R)
        {
            t[x].mi += dt;
            t[x].cmi += dt;
            t[x].dt += dt;
            t[x].ctdt++;
            return;
        }
        pushdown(x);
        int mid = (l + r) >> 1;
        if(L <= mid) modify_add(x<<1, l, mid, L, R, dt);
        if(R > mid) modify_add(x<<1|1, mid+1, r, L, R, dt);
        pushup(x);
    }

    void query(int x, int l, int r, int pos)
    {
        if(l == r)
        {
            ans.val = t[x].mi;
            ans.op = t[x].ctmi + t[x].ctdt;
            return;
        }
        pushdown(x);
        int mid = (l + r) >> 1;
        if(pos <= mid) query(x<<1, l, mid, pos);
        else query(x<<1|1, mid+1, r, pos);
    }

    void modify_min(int x, int l, int r, int L, int R, int dt)
    {
        if(L <= l && r <= R)
        {
            if(t[x].mi >= dt) return;
            if(t[x].cmi > dt)
            {
                t[x].mi = dt; t[x].ctmi++;
                return;
            }
            //如果取max的数小于最小值就不用管,大于次小值继续递归,在两个值之间
            //只修改最小值
            //次小值是严格次小,当然一定能继续递归啊,如果次小值存在的话
        }
        pushdown(x);
        int mid = (l + r) >> 1;
        if(L <= mid) modify_min(x<<1, l, mid, L, R, dt);
        if(R > mid) modify_min(x<<1|1, mid+1, r, L, R, dt);
        pushup(x);
    }
}T;

int main()
{
    n = read();
    for(int i=1; i<=n; i++)
    {
        val[i] = read();
    }
    T.build(1, 1, n);
    m = read();
    for(int i=1; i<=m; i++)
    {
        scanf("%s", c);
        if(c[0] == 'Q')
        {
            int pos = read();
            T.query(1, 1, n, pos);
            printf("%lld %d\n", ans.val, ans.op);
        }
        else 
        {
            int l = read(), r = read(), dt = read();
            if(c[0] == 'A' && dt) T.modify_add(1, 1, n, l, r, dt);
            if(c[0] == 'M') T.modify_min(1, 1, n, l, r, dt);
        }
    }
  
    return 0;
}
View Code

 Cat就是这样靠着Chen_jr大佬的题解苟且偷生的……

posted @ 2022-08-14 09:06  Catherine_leah  阅读(66)  评论(3编辑  收藏  举报
/* */