根据二进制建一棵01字典树,每个节点的答案等于左节点0的个数 * 右节点1的个数 * 2,遍历整棵树就能得到答案。
AC代码:
#include<cstdio>
using namespace std;
const int mod=998244353;
const int maxn=2;
struct node{
node *next[maxn]; //0 1节点
int cnt,level;
node(){
cnt=0; level=0;
next[0]=NULL; next[1]=NULL;
}
}*root;
long long pp[40];
void deal(){
pp[0]=0;
pp[1]=1;
for(int i=2;i<=30;++i)
pp[i]=pp[i-1]*2;
}
void Init(){
root=new node();
}
void insert_tree(int num){
node *p=root,*q;
for(int i=1;i<=30;++i){
int u=num&pp[i];
if(u!=0) u=1;
if(p->next[u]==NULL){
q=new node();
q->level=i;
q->cnt=1;
p->next[u]=q;
p=p->next[u];
}
else {
p->next[u]->cnt++;
p=p->next[u];
}
}
}
long long getAns(node *u){
if(u==NULL) return 0;
long long ans=0;
long long l=0,r=0,lev=0; //特别注意,可能会没有左右节点
if(u->next[0]!=NULL) {l=u->next[0]->cnt; lev=u->next[0]->level;}
if(u->next[1]!=NULL) {r=u->next[1]->cnt; lev=u->next[1]->level;}
ans=(ans+(l * r * pp[lev]) %mod)%mod;
for(int i=0;i<2;++i){
ans=(ans+getAns(u->next[i])%mod)%mod;
}
delete u;
return ans;
}
int main(){
deal();
int T,n,x,kase=1;
scanf("%d",&T);
while(T--){
Init();
scanf("%d",&n);
for(int i=0;i<n;++i) {
scanf("%d",&x);
insert_tree(x);
}
printf("Case #%d: %lld\n",kase++,getAns(root)*2%mod);
}
return 0;
}
如有不当之处欢迎指出!