题解:AT_arc216_c [ARC216C] Count Power of 2
-
O(n\log^2 n) - 双哈希
- 笛卡尔树
-
启发式优化
一个比较直接好想的做法。
~易得~性质一:一段区间的和最多不会超过
于是我们建出笛卡尔树,考虑在笛卡尔树上分治。令
因为笛卡尔树上的一个点代表一段区间的最大值,所以我们考虑包含该最大值的合法区间的个数,枚举最终的区间和
- 把左边的
pre_{i-1} 放入 unordered_map 里,再枚举右边的pre_i ,查询pre_i-B 在 unordered_map 里的个数。 - 把右边的
pre_{i} 放入 unorderedmap 里,再枚举左边的 $pre{i-1},查询 pre_{i-1}+B$ 在 unordered_map 里的个数。
直接做是
我们可以用启发式来优化,只枚举短的一边,另外一边通过递归放入 unordered_map 里。这样时间复杂度就优化到
对于
为了防止出题人卡我们的 unordered_map 导致我们 TLE,可以把插入和查询的值先乘上一个值再加上一个值,两个值可以在程序初始化时随机一下。
:::success[代码]
#include<bits/stdc++.h>
#define fi first
#define se second
#define pi pair<int,int>
typedef long long ll;
using namespace std;
const int N=2e5+100;
const int mod[2]={998244353,(int)1e9+9};
struct node{
ll fi,se;
ll lld(){
return (fi<<32)|se;
}
static ll ck0(ll x,ll y){
x+=y;
if(x<0) x+=mod[0];
if(x>mod[0]) x-=mod[0];
return x;
}
static ll ck1(ll x,ll y){
x+=y;
if(x<0) x+=mod[1];
if(x>mod[1]) x-=mod[1];
return x;
}
node operator+(const node &X)const{
return {ck0(fi,X.fi),ck1(se,X.se)};
}
node operator-(const node &X)const{
return {ck0(fi,-X.fi),ck1(se,-X.se)};
}
bool operator==(const node &X)const{
return fi==X.fi&&se==X.se;
}
node operator*(const ll &X)const{
return {fi*X%mod[0],se*X%mod[1]};
}
node operator*(const node &X)const{
return {fi*X.fi%mod[0],se*X.se%mod[1]};
}
};
int n;
ll a[N];
node b[N],pre[N];
ll ans;
int l[N],r[N],st[N],top;
int root;
struct CustomHash {
static uint64_t splitmix64(uint64_t x) {
x+=0x9e3779b97f4a7c15;
x=(x^(x>>30))*0xbf58476d1ce4e5b9;
x=(x^(x>>27))*0x94d049bb133111eb;
return x^(x>>31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM =
chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x+FIXED_RANDOM);
}
};
unordered_map<ll,int,CustomHash> mp;
node qmi(node x,ll y){
node res={1,1};
while(y){
if(y&1) res=res*x;
x=x*x; y>>=1;
}
return res;
}
void build(){
for(int i=1;i<=n;i++){
int last=0;
while(top&&a[st[top]]<a[i]) last=st[top],top--;
if(top) r[st[top]]=i;
else root=i;
l[i]=last;
st[++top]=i;
}
}
void dfs(int u,int tl,int tr,bool is=false){
if(!u) return ;
if(tr-u<u-tl){
dfs(r[u],u+1,tr,0);
dfs(l[u],tl,u-1,1);
node bas=b[u];
mp[pre[tl-1].lld()]++;
for(int s=0;s<=20;s++){
for(int i=u;i<=tr;i++){
node t=pre[i]-bas;
if(mp.find(t.lld())!=mp.end())
ans+=mp[t.lld()];
}
bas=bas*2;
}
mp[pre[tl-1].lld()]--;
for(int i=u;i<=tr;i++) mp[pre[i].lld()]++;
}else{
dfs(l[u],tl,u-1,0);
dfs(r[u],u+1,tr,1);
node bas=b[u];
mp[pre[u].lld()]++;
for(int s=0;s<=20;s++){
for(int i=tl;i<=u;i++){
node t=pre[i-1]+bas;
if(mp.find(t.lld())!=mp.end())
ans+=mp[t.lld()];
}
bas=bas*2;
}
mp[pre[u].lld()]--;
for(int i=tl;i<=u;i++) mp[pre[i].lld()]++;
}
if(!is) mp.clear();
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
b[i]=qmi({2,2},a[i]);
}
for(int i=1;i<=n;i++) pre[i]=pre[i-1]+b[i];
build();
dfs(root,1,n);
cout<<ans;
return 0;
}
:::