题目
https://gmoj.net/senior/#main/show/6829
https://gmoj.net/senior/#main/show/7038
想不到,半年之后,居然遇到了重题!
题解
首先证明一个结论:选取后序列的异或最小值只可能为序列排序后相邻元素的异或值的最小值。
证明:
设当前有 a ≤ b ≤ c a\le b\le c a≤b≤c ,现在只需证明 min ( a ⊕ b , b ⊕ c ) ≤ a ⊕ c \min(a\oplus b,b\oplus c)\le a\oplus c min(a⊕b,b⊕c)≤a⊕c 即可。
- 若它们的最高 m m m 位相等,这时 a ⊕ b , b ⊕ c , a ⊕ c a\oplus b,b\oplus c,a\oplus c a⊕b,b⊕c,a⊕c 的前 m m m 位都为 0 0 0 ,相等,因此考虑第 m + 1 m+1 m+1 位。
- 用 x , y , z x,y,z x,y,z 表示 a , b , c a,b,c a,b,c 的第 m + 1 m+1 m+1 位,现在分类讨论:
- 若 x = y < z x=y<z x=y<z ,则 min ( x ⊕ y , y ⊕ z ) = x ⊕ y = 0 < 1 = x ⊕ z \min(x\oplus y,y\oplus z)=x\oplus y=0<1=x\oplus z min(x⊕y,y⊕z)=x⊕y=0<1=x⊕z ;
- 若 x < y = z x<y=z x<y=z ,则 min ( x ⊕ y , y ⊕ z ) = y ⊕ z = 0 < 1 = x ⊕ z \min(x\oplus y,y\oplus z)=y\oplus z=0<1=x\oplus z min(x⊕y,y⊕z)=y⊕z=0<1=x⊕z 。
- 因此 ∀ a , b , c ∈ N , a ≤ b ≤ c , 都有 min ( a ⊕ b , b ⊕ c ) ≤ a ⊕ c \forall a,b,c\in N,a\le b\le c,\text{都有}\min(a\oplus b,b\oplus c)\le a\oplus c ∀a,b,c∈N,a≤b≤c,都有min(a⊕b,b⊕c)≤a⊕c 。
有了这个结论之后就好做了。
先将
a
a
a 从小到大排序。
设
f
i
f_i
fi 表示以
a
i
a_i
ai 结尾的合法序列的个数。
那么有状态转移方程
f
i
=
1
+
∑
1
≤
j
<
i
,
a
i
⊕
a
j
≥
x
f
j
f_i=1+\sum_{1\le j<i,a_i\oplus a_j\ge x}f_j
fi=1+1≤j<i,ai⊕aj≥x∑fj
这样子是
O
(
n
2
)
O(n^2)
O(n2) 的,考虑怎么优化。发现可以在这个异或上面做文章。
对排序后的序列建一棵 trie ,用它来优化
D
P
DP
DP ,这里不妨将
x
x
x减去
1
1
1 ,这样只用处理
a
i
⊕
a
j
>
x
a_i\oplus a_j>x
ai⊕aj>x 的情况了。
每次插入新的
a
i
a_i
ai 时,就先在 trie 上求出
f
i
f_i
fi 的值,然后再将
a
i
a_i
ai 加入 trie 。
求
f
i
f_i
fi 时,可以从上往下走,满足当前节点到根的二进制前缀异或上
a
i
a_i
ai 的相同长度前缀等于
x
x
x 的相同长度二进制前缀。
具体来说是这样的(用
x
′
,
a
i
′
x',a'_i
x′,ai′ 表示
x
x
x 和
a
i
a_i
ai 当前为上的数,值为
0
0
0 或
1
1
1 ):
- 若 x ′ = 0 x'=0 x′=0 ,这时若能使 a i ′ ⊕ a j ′ = 1 a'_i\oplus a'_j=1 ai′⊕aj′=1 ,就能将 f j f_j fj 累加入 f i f_i fi 中,因此加上以 a i ′ a'_i ai′ 的兄弟节点的子树内的 f f f 之和,接着走到 a i ′ a'_i ai′ 中;
- 若 x ′ = 1 x'=1 x′=1 ,这时只能使 a i ′ ⊕ a j ′ = 1 a'_i\oplus a'_j=1 ai′⊕aj′=1 ,因此走到 a i ′ a'_i ai′ 的兄弟节点上。
这样就做完了。
这道题目比较关键的地方是开头那个证明和用 trie 优化
D
P
DP
DP 。
CODE
gmoj 6829. 【2020.10.25提高组模拟】异或
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define trans(x) (x?1:0)
#define P 998244353
#define M 20000005
#define N 300005
ll x,a[N];int f[N],sum[M],son[M][2],s=1,id;
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
void qry(int k,ll a,ll num)
{
if(k==0||!num) return;
if(x&num) qry(son[k][trans(a&num)^1],a,num>>1);
else
add(f[id],sum[son[k][trans(a&num)^1]]),
qry(son[k][trans(a&num)],a,num>>1);
}
void inc(int k,ll a,ll num)
{
if(!num){add(sum[k],f[id]);return;}
int to=trans(a&num);
if(!son[k][to]) son[k][to]=++s;
inc(son[k][to],a,num>>1);
sum[k]=sum[son[k][0]]+sum[son[k][1]];
if(sum[k]>=P) sum[k]-=P;
}
int main()
{
freopen("xor.in","r",stdin);
freopen("xor.out","w",stdout);
int n,s,ans=0;
scanf("%d%lld",&n,&x);
for(int i=1;i<=n;++i) scanf("%lld",a+i);
if(!x)
{
ans=1;for(int i=1;i<=n;++i) ans=2LL*ans%P;
--ans;if(ans<0) ans+=P;printf("%d\n",ans);
return 0;
}
--x,sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
f[++id]=1,qry(1,a[i],1LL<<59),
inc(1,a[i],1LL<<59),add(ans,f[i]);
printf("%d\n",ans);
return 0;
}
gmoj 7038. 2021.04.01【2021省赛模拟】异或 (inception)
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
#define fo(i,l,r) for(i=l;i<=r;++i)
#define M 20000005
#define N 300005
const int P=998244353;
ll x,a[N];
int f[N],son[M][2],sum[M],s=1,cnt;
inline void add(int &x,int y){x+=y;if(x>=P) x-=P;}
inline int addi(int x,int y){x+=y;return x>=P?x-P:x;}
void solve(int k,ll now,ll num)
{
if(k==0||!num) return;
int wh=now&num?1:0;
if(x&num) solve(son[k][wh^1],now,num>>1);
else
{
add(f[cnt],sum[son[k][wh^1]]);
solve(son[k][wh],now,num>>1);
}
}
void inc(int k,ll x,ll num)
{
if(!num){add(sum[k],f[cnt]);return;}
int to=x&num?1:0;
if(!son[k][to]) son[k][to]=++s;
inc(son[k][to],x,num>>1);
sum[k]=addi(sum[son[k][0]],sum[son[k][1]]);
}
int main()
{
freopen("inception.in","r",stdin);
freopen("inception.out","w",stdout);
register int i;
int T,n,ans=0;
scanf("%d%d%lld",&T,&n,&x);
fo(i,1,n) scanf("%lld",a+i);
if(!x)
{
ans=1;
fo(i,1,n) ans=2LL*ans%P;
printf("%d\n",addi(ans,P-1));
return 0;
}
sort(a+1,a+n+1),--x;
fo(i,1,n)
{
f[++cnt]=1,solve(1,a[i],1LL<<59);
inc(1,a[i],1LL<<59),add(ans,f[i]);
}
printf("%d\n",ans);
return 0;
}