有兩種方法都可以拿到滿分
考慮枚舉
建兩個,要支持刪除操作
一顆維護左邊的信息
一顆維護右邊的信息
在枚舉的時候左邊的添加,右邊的刪除,可做到維護,建樹是
暴力的想法是兩個一起跑,枚舉在哪一位開始不一樣,前面的情況也都枚舉,這樣的情況最壞是的指數級別,可以拿到分
考慮優化這個過程
因爲每次只會刪去一條鏈,所以考慮這條鏈被刪去所帶來的影響
用表示到第位開始不一樣的答案
每次刪除時只少了一條鏈,所以把這條鏈的情況減去
增加時加上新的情況即可
這種方法相對來說代碼量要少很多
考慮枚舉
用一顆表示前面的信息
仍然考慮枚舉在哪一位開始不一樣
記表示前面所有在第位爲的串的總數
記表示上號節點有多少個不合法的對
記表示上經過的個數
由於枚舉的是,我們對的要求只有 枚舉到時,其在是還是
假設枚舉到的的值在位是
答案就是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;
}
如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧