题解 史莱姆B
- 有序数列中临项异或和最小值为数列中所有二元组异或和最小值
- \(a\oplus(a+b)\geqslant b\),证明考虑是否进位即可
变形成 \(a\leqslant b\) 时 \(b\oplus a\geqslant b-a\) 也许可以作为切入点
考虑 sub2 怎么写
令 \(t=\min{\{s_{i+1}-s_i\}}\),那么 \(w\geqslant t\) 时答案为 t
否则 \(t\leqslant \dfrac{2^V}{|S|}\),枚举答案的复杂度是 \(O(2^V)\) 的
那么 sub3 可以只在集合大小发生变化时重算,复杂度调和级数知是 \(O(V2^V)\) 的
然后正解:
对于一对相邻 \((i, j)\),有用的 \(x\) 只有 \(O(V)\) 个,且一定是使 \(i+x\) 或 \(j+x\) 的后 \(k\in[0, V]\) 位为 0
打表应该是一个比较好的发现方法
证明的话考虑 \(i+x\) 后 \(k\) 位为 0,让 \(x\) 继续变大
那么答案想更优的话 \(j\) 一定产生了向第 \(k+1\) 位的进位
考虑对 \(y\) 操作的最低位
若操作了某一位,则一定会一直操作使其一直进到第 \(k+1\) 位
操作最低为 1 位一定更优
那么这样 \(j+x\) 后 \(k+1\) 位就都为 0 了
于是用 set 对所有二元组 \((x, (i+x)\oplus(j+x))\) 维护单调栈即可
复杂度 \(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int v, n;
namespace force{
int sta[N], top;
void solve() {
for (int i=1,op,w; i<=n; ++i) {
op=read(); w=read();
if (op==1) sta[++top]=w;
else {
int ans=INF;
for (int x=0; x<=w; ++x)
for (int i=1; i<=top; ++i)
for (int j=i+1; j<=top; ++j)
ans=min(ans, (x+sta[i])^(x+sta[j]));
printf("%d\n", ans);
}
}
}
}
namespace task{
set<int> s;
set<pair<int, int>> ans;
void ins(int i, int j, int x) {
int val=(i+x)^(j+x);
for (auto it=ans.lower_bound({x, val}); it!=ans.end()&&it->sec>=val; it=ans.erase(it)) ;
auto pre=ans.lower_bound({x, val});
if (pre!=ans.begin() && (--pre)->sec<=val) return ;
ans.insert({x, val});
}
void add(int i, int j) {
int lim=1;
ins(i, j, 0);
for (int k=0,x; k<=v; ++k,lim<<=1) {
x=lim-(i&(lim-1));
ins(i, j, x);
x=lim-(j&(lim-1));
ins(i, j, x);
}
}
void solve() {
for (int i=1,op,w; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
op=read(); w=read();
if (op==1) {
auto it=s.lower_bound(w);
if (it!=s.end()) add(w, *it);
if (it!=s.begin()) add(w, *(--it));
s.insert(w);
}
else printf("%lld\n", (--ans.upper_bound({w, INF}))->sec);
// cout<<"ans: "; for (auto it:ans) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;
}
}
}
signed main()
{
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
v=read(); n=read();
// force::solve();
task::solve();
return 0;
}