机房测试:race(trie)

题目:

分析:

先不考虑天数的限制,直接对每一个人建一颗trie。

对于每一个人来说,他的x的贡献来源于trie树上所有在他右边的点(都比他大)。

将每一个子树所有的叶子结点记为f,x^2=(f1+f2+……fx)^2

将右式拆开看:f i *f i + f i * f j *2(枚举i,j统计贡献,*2是因为 i 和 j 可以交换)

再加上天数的限制:每一个人的A[i]异或上天数 j,相当于trie树的形态发生改变了(01边交换)

对于1来说,又多了一部分贡献,也就是说,对于右子树来说,当且仅当交换1条边会产生贡献,而其它的m-1条边随便交换,所以是f i * f i *(2^(m-1))

对于i,j来说,当两条边被交换了,才会产生贡献,所以是 f i * f j *2 *(2^(m-2))

dfs一遍trie树,统计答案即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200005
#define ri register int
const ll mod = 1e9+7;
int go[N*30][2],siz[N*30],a[N],n,m,cnt=0;
ll ans=0;
void add(int x)
{
    int now=0;
    for(ri i=m-1;i>=0;--i){
        bool c=x&(1<<i); //这里的c一定要用bool,因为可能与出其它东西来,导致不是0和 1
        if(!go[now][c]) go[now][c]=++cnt;
        now=go[now][c]; siz[now]++;
    }
}
void dfs(int x,ll sum,ll num)//num相当于是其它子树的siz
{
    ll sz=siz[go[x][1]],tmp=0;
    tmp=sz*sz %mod *(1<<m-1) %mod; tmp=(tmp + sz*num %mod *(1<<m-1) %mod) %mod;
    if(go[x][0]) dfs(go[x][0],(sum+tmp) %mod, num+sz);

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

    if(!go[x][0] && !go[x][1]) ans^=sum;//叶子节点
}
int main()
{
    freopen("race.in", "r", stdin);
      freopen("race.out", "w", stdout);
    scanf("%d%d",&n,&m);
    for(ri i=1;i<=n;++i) scanf("%d",&a[i]),add(a[i]);
    dfs(0,0,0);
    printf("%lld\n",ans);
}
/*
3 2
0 1 2
*/

原文地址:https://www.cnblogs.com/mowanying/p/11779626.html

时间: 2024-08-30 12:06:55

机房测试:race(trie)的相关文章

机房测试9.23

题解之前 今天还行啊. Set 肯定要取模. 开始还在想vector这种操作,后来一个dalao发现一定有解,然后有发现一定有一种答案是连续的一段区间,于是就切掉了. 看了题解才发现我们只是运气好. 前缀和如果有n取值,就选%n=0的那一个,不然至多只剩n-1个取值,然而又有n个前缀和. 所以必然有两个相等,输出这之间的下标即可. #include<cstdio> #include<cctype> #include<cstring> #define FN "s

[CSP-S模拟测试]:Race(数学+Trie树)

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

机房测试1:big(贪心+Trie树)

题目: 分析: 考虑最暴力的办法:枚举选哪个数,枚举对手在哪个时间变化,然后统计答案. 对于异或这一类问题,考虑区间异或可以抵消重复区间,维护一个前缀异或和:pre[i]表示1~i的异或和,suf[i]表示i~n的异或和. 将对手的式子化简,2*x即将x向左移一位,/( 2^n )为向右移n位,+2*x ,%2^n类似. 模拟一下:12345 -> 123450 -> 123451 -> 23451 每次枚举对手要变的时间i,最后的值即为:work ( pre[i] ^ x ) ^ su

Host1Plus主机商8个机房测试IP体验

Host1Plus商家也算是比较老的海外主机商(英国),当初进入中国市场也是比较早的,记得那时候支持支付宝的海外主机商尤其的深受用户喜欢.因为我 们大部分网友.站长最多有一个支付宝,很少有双币信用卡或者贝宝支付美元的能力.不过后来几年,HOST1PLUS商家逐渐的落寞,主要原因在于其他商家 的出现,以及他们本身营销能力和产品的策略. 尤其是提供的虚拟主机和VPS主机,价格和方案不是太符合中国用户的口味,配置比较低,而且机房较 少.不过商家应该意识到这一点,在最近2年改动挺大的,今天公司业务正好有

[20191004机房测试] ZGY的早餐

ZGY 每天早上要从宿舍走路到机房,顺便从学校小卖部购买早饭,当然机智的 ZGY 一定会走最短路 学校的路可以看成一无向联通张图,图上有 n 个点,m 条边,每一个点都有一个唯一的编号 1~n 每一条边有一个边权,表示两个点之间的距离,ZGY 的宿舍在 S 点,机房在 T点,而小卖部在 H 点 现在 ZGY 想知道从宿舍经过小卖部到达机房的最短距离 不过因为在这个世界上有 Q个 ZGY,所以你必须回答 Q 个问题 很棒的数据分治题 读入里面说了会读入测试点编号-- 其实是很明显的暗示了-- 一半

机房测试9.22

题解之前 中秋只有一天假! build 以为并查集水题,发现有历史版本,于是可持久化并查集一波(爆0了) 其实有简单点的做法:用二元组记录每一次变化的siz和fa,二分查询即可. 我还在肝可持久并查集,所以就没有代码了. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> using namespace std; co

机房测试10.6

砖块 很简单的水题,写起来也很简单. 模拟写的很短,是否也是码力的体现? #include<cstdio> #include<cstring> #include<map> #define FN "block" int mp[1105][1105],dao,ans; char s[105]; int x,y,dx[4]={0,0,-1,1},dy[4]={1,-1,0,0}; std::map<char,int> m; void init(

[机房测试]数字谜题

小 X 同学有很强的计算能力,现在他正在玩一个游戏. 现在有一个正整数 x,每次操作他会将当前的数变为这个数写成二进制后 1 的个数 小 X 不断的进行操作,直到这个数变成 1 为止 由于小 X 的计算能力很强,他现在给出一 n 他想知道有多少不超过 n 的正整数会在 k 次操作后变成 1 由于答案可能很大,请对 1000000007 取模 因为数据范围是\(2^{1000}\),所以一次操作后最多就只有1000个1了 因此直接暴力处理出1~1000所有数变为1的操作次数,把次数为 \(k-1\

机房测试1:string(线段树)

题目: 分析: 暴力:每一次对区间暴力排序. 优化:如果可以知道一个区间中有哪种字符,这些字符分别有多少个,就可以直接按字典序枚举,将它们快速地插入区间中了. 题中有一个重要信息:只有小写字母,即只有26种字符. 第一种方法: 可以用一个线段树来维护,每个节点储存26个字符在这个区间中的对应情况.每次修改,就query统计出所求区间每一种字符的个数. 然后再区间修改,具体见代码. #include<bits/stdc++.h> using namespace std; #define mid