根据二进制建一棵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; }
如有不当之处欢迎指出!