题解:P17723 [SCP-S 2026] 函数 / func
Aurelia_Veil · · 题解
题解:P17723 [SCP-S 2026] 函数 / func
这道题真不错吧……
注:题解使用 AI 进行学术性检查和笔误检查,所有文本皆本人所写,贡献为 AI 的三倍以上。
part 1:
首先,对于一段区间
- 若
m 不是2 的非负整数次幂:令k 为m 在二进制下的最高位,因为不是2 的非负整数次幂,所以2^k < m ,由于0 到m-1 都在S 中,因此2^k 也在S 中,所以一定会和2^k 重复,所以第k 位按位或起来就不会有变化;对于非最高位的第i 位,若这一位为1 ,则都会与m - 2^k 重复(因为m - 2^k < m 所以m - 2^k 在S 中),所以这些位也不会对答案产生改变。 - 若
m 是2 的非负整数次幂:令k 为m 在二进制下的最高位,则所有小于m 的非负整数第k 都不可能为1 ,所以符合条件。
所以答案就被拆成了两个部分:所有区间的按位或的和、部分
part 2:
那么哪些区间会对答案产生贡献呢?
由 part 1 可知,只有当
- 这一段区间的所有数二进制的第
k 位都为0 (保证有贡献); - 区间包含
0 到2^k-1 的所有数(保证\operatorname{MEX}(S)=2^k )。
于是问题变成了:对每个
如何统计?观察到第
part 3:
我们该如何计算所有区间的按位或的和呢?我们能够发现按位或运算每一位都是独立的,所以我们每一位进行计算就可以了。
对于第
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e6+111;
int a[N];
int n;
int pre[N];
int t[N];
signed main(){
// freopen("func.in","r",stdin);
// freopen("func.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
unsigned long long ans=0;
for(int i=0;i<=31;i++){
int top=0;
unsigned long long cnt=0;
for(int j=1;j<=n;j++){
if(a[j]&(1ll<<i)){
cnt+=(j-1-top)*1llu*(j-top)/2;
top=j;
}
}
if(top==0){
continue;
}
cnt+=(n+1-top)*1llu*(n-top)/2;
ans+=((n+1)*1llu*n/2-cnt)*1llu*(1ll<<i);
}
queue<int>q;
for(int i=0;i<=20;i++){//log N
while(!q.empty()){
pre[a[q.front()]]=0;
t[a[q.front()]]--;
q.pop();
}
int top=0;
int cnt=0;
for(int j=1;j<=n;j++){
if(a[j]&(1<<i)){
while(!q.empty()){
pre[a[q.front()]]=0;
t[a[q.front()]]--;
q.pop();
}
cnt=0;
top=j;
continue;
}
if(a[j]<(1<<i)){
q.push(j);
pre[a[j]]=j;
if(t[a[j]]==0){
cnt++;
}
t[a[j]]++;
}
while((!q.empty())&&pre[a[q.front()]]>q.front()){
t[a[q.front()]]--;
q.pop();
}
if(cnt<(1<<i)){
continue;
}
// cerr<<i<<" "<<j<<" "<<q.front()<<" "<<top<<"\n";
ans+=(q.front()-top)*1llu*(1<<i);
}
}
printf("%llu",ans);
return 0;
}
感谢你的观看,芜湖。