有两种方法都可以拿到满分
考虑枚举
建两个,要支持删除操作
一颗维护左边的信息
一颗维护右边的信息
在枚举的时候左边的添加,右边的删除,可做到维护,建树是
暴力的想法是两个一起跑,枚举在哪一位开始不一样,前面的情况也都枚举,这样的情况最坏是的指数级别,可以拿到分
考虑优化这个过程
因为每次只会删去一条链,所以考虑这条链被删去所带来的影响
用表示到第位开始不一样的答案
每次删除时只少了一条链,所以把这条链的情况减去
增加时加上新的情况即可
这种方法相对来说代码量要少很多
考虑枚举
用一颗表示前面的信息
仍然考虑枚举在哪一位开始不一样
记表示前面所有在第位为的串的总数
记表示上号节点有多少个不合法的对
记表示上经过的个数
由于枚举的是,我们对的要求只有 枚举到时,其在是还是
假设枚举到的的值在位是
答案就是cnt[原本串中和的前面的位相同]不合法的对数
具体可看代码
因为这种好实现些,所以就写的这种
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月23日 星期一 19时48分07秒
*******************************/
#include <cstdio>
#include <fstream>
#include <cstring>
#define ll long long
#define reset(x) memset(x,0,sizeof(x))
using namespace std;
const int maxn = 3000006;
const int lim = 29;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int T,n,v,tot;
int ch[maxn][2],s[lim+1][2];
ll sum[maxn],cnt[maxn];
void insert (int rt,int v,int d)
{
if (d<0) return;
int c=(v>>d)&1;
if (!ch[rt][c]) ch[rt][c]=++tot;
rt=ch[rt][c];
++cnt[rt],sum[rt]+=++s[d][c];
insert(rt,v,d-1);
}
ll query (int rt,int v,int d)
{
if (d<0) return 0;
int c=(v>>d)&1,p=ch[rt][c^1];
return cnt[p]*s[d][c^1]-sum[p]+query(ch[rt][c],v,d-1);
}
int main()
{
cin>>T;
while (T--){
cin>>n;
ll ans=tot=0;
reset(sum),reset(s),reset(ch),reset(cnt);
for (int i=1;i<=n;++i){
cin>>v;
insert(0,v,lim);
ans+=query(0,v,lim);
}
printf("%lld\n",ans);
}
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧