*题解:P6105 [Ynoi2010] y-fast trie

原题链接

解析

容易发现所有元素对 \(C\) 取模不影响答案,所以取模后进行分类讨论。

取模后对于集合内任意两元素 \(x,y\),有 \(x + y < 2C\),若 \(x + y \ge C\),则 \((x + y) \bmod C=x+y-C\),此时我们显然希望 \(x,y\) 尽可能大,所以取最大值和次大值。

否则,在 \(x\) 确定的情况下我们希望找到使得 \(x + y < C\) 的最大 \(y\)。考虑将这样的 \(x,y\) 配对并将贡献存到一个 multiset 里,但是这样的话在修改时会影响 \(O(|S|)\) 个位置。不能维护所有的配对,那应该维护什么样的配对呢?对于最优配对 \((x,y)\),必定有 \((y,x)\) 与之对应,否则会出现更优秀的配对,能不能只维护这些双向配对的贡献呢?

考虑插入,我们需要判断新加入的元素 \(x\) 能否创造一个双向配对。先找到 \(x\) 的配对 \(y\),再找到 \(y\) 的配对 \(z\)\((x,y)\)\((y,z)\) 更优当且仅当 \(x > z\),所以如果 \(x > z\),就可以建立 \(x\)\(y\) 的双向链接,同时如果 \(y\)\(z\) 已经建立了双向链接,则需要切断。

考虑删除,我们需要判断将要删除的元素 \(x\) 是否建立了双向配对,如果是则需要让配对的元素 \(y\) 重新寻找配对,类似插入的过程。

时间复杂度 \(O(n\log n)\)

代码

#include <bits/stdc++.h>
#define ls(p) ((p) << 1)
#define rs(p) (((p) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 600 + 5,M = 5e5,mod = 998244353;
int n,c;
multiset<int> s,res;
map<int,int> cnt;
map<pair<int,int>,int> m;
int find(int x,bool f){//f 表示 x 是否在集合内
	if(x == -1) return -1;
	auto it = s.upper_bound(c - 1 - x); 
	if(it == s.begin()) return -1;
	it--;
	if(*it == x && cnt[x] == 1 && f){
		if(it == s.begin()) return -1;
		it--;
	}
	return *it;
}
void insert(int x){
	int y = find(x,0),z = find(y,1);
	if(y != -1 && z <= x){
		if(m[{min(y,z),max(y,z)}]){
			res.erase(res.find(y + z));
			m[{min(y,z),max(y,z)}] = false;
		}
		m[{min(y,x),max(y,x)}] = true;
		res.insert(x + y);
	}
	s.insert(x);
	cnt[x]++;
}
void erase(int x){
	int y = find(x,1),z = find(y,1);
	s.erase(s.find(x));
	cnt[x]--;
	if(z == x){
		if(m[{min(y,x),max(y,x)}]){
			res.erase(res.find(x + y));
			m[{min(y,x),max(y,x)}] = false;
		}
		z = find(y,1);
		int k = find(z,1);
		if(z != -1 && k <= y){
			res.insert(z + y);
			m[{min(y,z),max(y,z)}] = true; 
		}
	}
}
int ask(){
	if(s.size() < 2){
		return -1;
	}
	int ans = max(*s.rbegin() + *next(s.rbegin()) - c,res.size() == 0 ? 0 : *res.rbegin());
	return ans;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	cin>>n>>c;
	int l = 0;
	while(n--){
		int op,x;
		cin>>op>>x;
		x ^= l;
		x %= c;
		if(op == 1){
			insert(x);
		}else{
			erase(x);
		}
		int ans = ask();
		if(ans == -1){
			l = 0;
			cout<<"EE\n";
		}else{
			l = ans;
			cout<<ans<<'\n';
		}
	}
	return 0;
}
posted @ 2025-11-21 09:51  yutar  阅读(7)  评论(0)    收藏  举报