其他分享
首页 > 其他分享> > 开坑难填之A层邀请赛1

开坑难填之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分。然后,吉斯机线段树是什么鬼啊,容我先去学习一下……

 

标签:ch,const,int,ll,开坑,maxn,ans,邀请赛
来源: https://www.cnblogs.com/Catherine2006/p/16584801.html