11.1 T3.race(Trie+x^2的转化)

题目描述
一年一度的运动会开始了. 有N个选手参赛, 第i个选手有一个能力值Ai, 比赛一共进行了 2^M天. 在第j天(0⩽ j⩽2^M−1 )的比赛中, 第i个选手的得分为A[i] xor j, 然后从大到小排名, 排名为x(x从0开始)的同学会获得 x^2的积分, 你需要求出每个同学最后总的积分和q[i]模 1e9+7 的结果p[i].
为了避免输出文件过大, 你只要输出p[i]的异或和即可.

输入格式
第一行三个整数, 分别代表N, M.
接下来一行N个整数, 第i个数代表A[i].
输出格式
一个整数代表答案.

样例
race.in
3 2
0 1 2
race.out
8

数据范围
对于10%的数据, M <= 5.
对于30%的数据, N <= 100.
对于50%的数据, N <= 1000.
对于100%的数据, N <= 200000, M <= 30, A[i] < 2^M .

分析:
我问所有会做的人:这道题怎么做啊?
他们都说:你先做这道题:管道取珠,之后就会了

那好吧,只好先写这道题

考虑怎么求出x的答案.
平方相当于是枚举两个人(可以相同), 把这两个人同时排在x前面的天数计入答案
那么对于x, 如果我们求出f[i]也就是能力值的二进制中第i+1到M-1位都和他相等且第i位不同的人有多少个, 那么这些人是否排在他前面只由第i位确定, 一共有2^(M-1)天,
而且不需要从这些人中枚举两个人了, 因为直接平方即可
我们只要枚举i和j, f[i]这些人和f[j]这些人同时排在他前面的天数为2^(M-2),
所以把2*f[i]*f[j]*2^(M-2)计入答案. 具体实现有很多方法, 可以用trie树实现.

在看std的时候看到这样一句:assert(0);

定义

assert宏的原型定义在

#include <assert.h>
void assert( int expression );

assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行。

//这里写std
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<map>
#define ll long long

using namespace std;

const int N=200003;
const int mod=1000000007;
int n,m,A,ch[N*30][2],tot=0,sz[N*30];
ll ans=0;

void insert(int A)
{
    int now=0;
    for (int i=m-1;i>=0;i--)
    {
        int x=(A>>i)&1;
        if (!ch[now][x]) ch[now][x]=++tot;
        now=ch[now][x];
        sz[now]++;                 //每个节点上的子串个数 
    }
}

void dfs(int x,ll sum,ll num)
{
    ll tmp,size;

    size=sz[ch[x][1]],tmp=0;
    tmp=size*size%mod*(1<<m-1)%mod;
    tmp=(tmp+size*num%mod*(1<<m-1)%mod)%mod;
    if (ch[x][0]) dfs(ch[x][0],(sum+tmp)%mod,num+size);

    size=sz[ch[x][0]],tmp=0;
    tmp=size*size%mod*(1<<m-1)%mod;
    tmp=(tmp+size*num%mod*(1<<m-1)%mod)%mod;
    if (ch[x][1]) dfs(ch[x][1],(sum+tmp)%mod,num+size);

    if (!ch[x][0]&&!ch[x][1]) ans^=sum;
}

map<int,bool> vis;

int main()
{
    freopen("race.in","r",stdin);  
    freopen("race.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) 
    {
        scanf("%d",&A);
        insert(A);
        if (vis.count(A)) return 0;
        vis[A]=1;
    }
    dfs(0,0,0);
    printf("%lld",ans);
    return 0; 
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值