背叛 仇恨 消极 如刀子刺穿了铁心 嘲笑 嗤之以鼻 漠然后只剩下孤寂

test20

异或xor

首先 Bob 可以将 \(a\) 两两匹配,永远拿跟 Alice 匹配的另一个,现在问题变成了最小化 \(\{a_{l_i}\oplus a_{r_i}\}\)

比较经典的从高位到低位贪心,先考虑第一位,\(0/1\) 的数量都是偶数的话答案的这一位可以是 \(0\),否则必须拿至少一对第一位不相等的,因为可以做到别的对这一位相等所以一定是特殊对贡献答案,不妨考虑异或值最小的特殊对作为答案。如果第一位可以是 \(0\),那就考虑下一位,历史前缀相同的归为一类就是跟刚刚一样的问题了,如果存在不能完美划分的就取 \(\min\) 贡献的 \(\max\) 作为答案输出即可,实现可以在 trie 上。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair

using namespace std;

const int N=200005, M=10000005;

int id, T, n, a[N], val[M], son[M][2], res[M], tot=1;

void insert(int x) {
	int p=1;
	dn(i,29,0) {
		int to=(x>>i)&1;
		if(!son[p][to]) son[p][to]=++tot;
		p=son[p][to], ++val[p], res[p]=((x>>i)<<i);
	}
}

int rmq(int v,int p) {
	if(!son[p][0]&&!son[p][1]) return res[p]^v;
	if(!son[p][1]) return rmq(v,son[p][0]);
	if(!son[p][0]) return rmq(v,son[p][1]);
	if((res[son[p][0]]^v)<(res[son[p][1]]^v)) return rmq(v,son[p][0]);
	return rmq(v,son[p][1]);
}

int find(int p,int rt) {
	if(!son[p][0]&&!son[p][1]) return rmq(res[p],rt);
	int l=1e18, r=1e18;
	if(son[p][0]) l=find(son[p][0],rt);
	if(son[p][1]) r=find(son[p][1],rt);
	return min(l,r);
}

pii ans(int p,int d) {
	if(!p) return mp(0,1e9);
	if(val[son[p][0]]%2==0) {
		pii l=ans(son[p][0],d+1);
		pii r=ans(son[p][1],d+1);
		if(l.second<r.second||l.second==r.second&&l.first>r.first) return l;
		return r;
	}
	int ret=find(son[p][1],son[p][0]);
	return mp(ret,d);
}

void mian() {
	memset(val,0,sizeof(val));
	memset(son,0,sizeof(son));
	memset(res,0,sizeof(res));
	cin >> n, tot=1;
	up(i,1,2*n) cin >> a[i], insert(a[i]);
	cout << ans(1,0).first << '\n';
}

signed main() {
	freopen("xor.in","r",stdin);
	freopen("xor.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> id >> T;
	while(T--) mian();
	return 0;
}


方sq

\(d_i\) 表示 \(i\) 收到的 操作 \(2\) 的次数-操作 \(1\) 的次数,显然我们只关心 \(d_i\) 的历史最小值和最终值。知道历史最小值后可以暴力还原,知道最终值可以用快速幂,用扩展欧几里得定理处理指数即可。但是话又说回来了刷新底线操作 \(1\) 反正是有限势能的,不妨就暴力去做,具体而言操作 \(2\) 正常打 lazytag,操作 \(1\) 就是优先下降 lazytag 如果不够了就暴力去做,还要额外维护一个区间存不存在 \(a_i>1\) 这样。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

const int N=300005, P=998244353, phi=P-1;

int id, T, n, m, a[N], d[N<<2], tag[N<<2];

int ksm(int a,int b,int P) {
	int ret=1;
	for( ; b; b>>=1) {
		if(b&1) ret=ret*a%P;
		a=a*a%P;
	}
	return ret;
}

inline void tup(int p) {
	tag[p]=tag[ls(p)]|tag[rs(p)];
}

inline void tdn(int p) {
	if(!d[p]) return;
	d[ls(p)]+=d[p], d[rs(p)]+=d[p], d[p]=0;
}

void build(int p=1,int s=1,int e=n) {
	d[p]=0;
	if(s==e) { tag[p]=(a[s]>1); return; }
	int mid=(s+e)>>1;
	build(ls(p),s,mid), build(rs(p),mid+1,e);
	tup(p);
}

void add(int l,int r,int p=1,int s=1,int e=n) {
	if(l<=s&&e<=r) { ++d[p]; return; }
	tdn(p);
	int mid=(s+e)>>1;
	if(l<=mid) add(l,r,ls(p),s,mid);
	if(r>mid) add(l,r,rs(p),mid+1,e);
}

void lower(int p,int s,int e) {
	if(!tag[p]) return;
	if(d[p]>0) { --d[p]; return; }
	if(s==e) { a[s]=sqrt(a[s]), tag[p]=(a[s]>1); return; }
	tdn(p);
	int mid=(s+e)>>1;
	lower(ls(p),s,mid), lower(rs(p),mid+1,e);
	tup(p);
} 

void del(int l,int r,int p=1,int s=1,int e=n) {
	if(l<=s&&e<=r) { lower(p,s,e); return; }
	tdn(p);
	int mid=(s+e)>>1;
	if(l<=mid) del(l,r,ls(p),s,mid);
	if(r>mid) del(l,r,rs(p),mid+1,e);
	tup(p);
}

void dfs(int p=1,int s=1,int e=n) {
	if(s==e) {
		int u=(d[p]<=29)?(1ll<<d[p]):(ksm(2,d[p],phi)+phi);
		cout << ksm(a[s],u,P) << ' ';
		return;
	}
	tdn(p);
	int mid=(s+e)>>1;
	dfs(ls(p),s,mid), dfs(rs(p),mid+1,e);
}

void mian() {
	cin >> n >> m;
	up(i,1,n) cin >> a[i];
	build();
	while(m--) {
		int opt, l, r;
		cin >> opt >> l >> r;
		if(opt==1) del(l,r);
		if(opt==2) add(l,r);
	}
	dfs(1), cout << '\n';
}

signed main() {
	freopen("sq.in","r",stdin);
	freopen("sq.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> id >> T;
	while(T--) mian();
	return 0;
}


括号游戏bracket

考虑一个形态的序列怎么计算答案,\(\text{(...(,,,(,,,))...(,,,)...)}\) 发现对于最外面的一对括号 \(\text{.}\) 的部分需要匹配并且独立出来惹,只要考虑长度为 \(n\) 的序列的方案书 \(f_n\) 即可求解出方案数。

\(f\) 是卡特兰数,有 \(f_{n}=\frac{1}{n+1}\binom{2n}{n}\),这个可以反射容斥算(就是将军饮马)。

然后再考虑一下维护每队存在括号的 \(\text{.}\) 数,发现关心刚加入的时候的 \(.\) 数以及过去包含它的最小(二维偏序的意思)括号对,前者可以线段树维护区间加区间最小值,后者可以线段树二分。午休的时候 yy 了一下,快速找父亲可以时光倒流然后并查集来着。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)

using namespace std;

const int N=400005, inf=1e13, P=998244353;

int id, T, n, a[N], b[N], t[N], tr[N<<2], stk[N], top, Rmq, Jury;
int res[N], pre[N], f[N], g[N], mul[N], inv[N], cnt[N<<2], tag[N<<2];

inline int C(int n,int m) {
	if(m<0||n<m) return 0;
	return mul[n]*inv[m]%P*inv[n-m]%P;
}

int ksm(int a,int b=P-2) {
	int ret=1;
	for( ; b; b>>=1) {
		if(b&1) ret=ret*a%P;
		a=a*a%P; 
	} 
	return ret;
}

void build(int p=1,int s=1,int e=n+1) {
	tr[p]=inf;
	if(s==e) return;
	int mid=(s+e)>>1;
	build(ls(p),s,mid), build(rs(p),mid+1,e);
}

void updata(int x,int p=1,int s=1,int e=n+1) {
	if(s==e) { tr[p]=stk[x]; return; }
	int mid=(s+e)>>1;
	if(x<=mid) updata(x,ls(p),s,mid);
	if(x>mid) updata(x,rs(p),mid+1,e);
	tr[p]=min(tr[ls(p)],tr[rs(p)]);
}

int lower(int v,int p=1,int s=1,int e=n+1) {
	if(s==e) return tr[p];
	int mid=(s+e)>>1;
	if(tr[rs(p)]<v) return lower(v,rs(p),mid+1,e);
	return lower(v,ls(p),s,mid);
}

int upper(int l,int r=n+1,int p=1,int s=1,int e=n+1) {
	if(tr[p]==inf) return 0;
	int mid=(s+e)>>1, ret=0;
	if(l<=s&&e<=r) {
		if(s==e) return s;
		if(tr[ls(p)]<inf) return upper(l,r,ls(p),s,mid);
		return upper(l,r,rs(p),mid+1,e);
	}
	if(l<=mid) ret=upper(l,r,ls(p),s,mid);
	if(!ret) ret=upper(l,r,rs(p),mid+1,e);
	return ret;
}

inline void Tup(int p) {
	if(tr[ls(p)]==tr[rs(p)]) {
		tr[p]=tr[ls(p)];
		cnt[p]=cnt[ls(p)]+cnt[rs(p)];
	}
	else if(tr[ls(p)]<tr[rs(p)]) {
		tr[p]=tr[ls(p)], cnt[p]=cnt[ls(p)];
	}
	else tr[p]=tr[rs(p)], cnt[p]=cnt[rs(p)];
}

inline void Tdn(int p) {
	if(tag[p]) {
		tag[ls(p)]+=tag[p], tr[ls(p)]+=tag[p];
		tag[rs(p)]+=tag[p], tr[rs(p)]+=tag[p];
		tag[p]=0;
	}
}

void Build(int p=1,int s=1,int e=2*n) {
	tag[p]=0;
	if(s==e) { tr[p]=tag[p]=0, cnt[p]=1; return; }
	int mid=(s+e)>>1;
	Build(ls(p),s,mid), Build(rs(p),mid+1,e);
	Tup(p);
}

void add(int l,int r,int p=1,int s=1,int e=2*n) {
	if(l<=s&&e<=r) {
		tr[p]++, tag[p]++;
		return;
	}
	Tdn(p);
	int mid=(s+e)>>1;
	if(l<=mid) add(l,r,ls(p),s,mid);
	if(r>mid) add(l,r,rs(p),mid+1,e);
	Tup(p);
}

void count(int l,int r,int p=1,int s=1,int e=2*n) {
	if(l<=s&&e<=r) {
		if(tr[p]<Rmq) Rmq=tr[p], Jury=cnt[p];
		else if(tr[p]==Rmq) Jury+=cnt[p];
		return;
	}
	Tdn(p);
	int mid=(s+e)>>1;
	if(l<=mid) count(l,r,ls(p),s,mid);
	if(r>mid) count(l,r,rs(p),mid+1,e);
}

void mian() {
	memset(res,0,sizeof(res));
	cin >> n;
	up(i,1,n) cin >> a[i] >> b[i], t[a[i]]=i, t[b[i]]=-i;
	build(), stk[top=1]=0, updata(top);
	up(i,1,2*n) {
		if(t[i]>0) {
			pre[t[i]]=lower(t[i]);
			stk[++top]=t[i], updata(top);
		}
		else stk[top]=inf, updata(top--);
	}
	Build();
	up(i,1,n) {
		Jury=0, Rmq=inf, count(a[i],b[i]);
		res[i]=(b[i]-a[i]+1-Jury)/2, add(a[i],b[i]);
	}
	int ans=f[n]; a[0]=0, b[0]=2*n+1;
	cout << ans << ' ';
	up(i,1,n) {
		int j=pre[i];
		(ans*=g[(b[j]-a[j]-1)/2-res[j]])%=P;
		res[j]+=(b[i]-a[i]+1)/2-res[i];
		(ans*=f[(b[j]-a[j]-1)/2-res[j]])%=P;
		(ans*=f[(b[i]-a[i]-1)/2-res[i]])%=P;
		cout << ans << ' ';
	} cout << '\n';
}

signed main() {
	freopen("bracket.in","r",stdin);
	freopen("bracket.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	n=1e5, mul[0]=inv[0]=inv[1]=1;
	up(i,1,2*n) mul[i]=mul[i-1]*i%P;
	up(i,2,2*n) inv[i]=inv[P%i]*(P-P/i)%P;
	up(i,2,2*n) inv[i]=inv[i-1]*inv[i]%P;
	up(i,0,n) f[i]=ksm(i+1)*C(2*i,i)%P, g[i]=ksm(f[i]);
	cin >> id >> T;
	while(T--) mian();
	return 0;
}

连通图connect

原来 int 一个函数不写返回值但是没有用来运算也可能会 re。

\([1,p],(p,n]\) 答案独立可以分开处理,下面考虑怎么做前缀。只有加边的话可以考虑对操作分块,询问按 \(p\) 排序,双指针并查集加前面的块边,然后对于每个查询额外加不超过 \(\sqrt n\) 条边,并查集/dfs 就可以做了。

不是我喜欢的删除直接不删,对于块中要删除但以前已经加了的边,考虑最开始不加入改成加边操作即可。

#include<bits/stdc++.h>
#define int long long
#define up(i,l,r) for(int i=l; i<=r; ++i)
#define dn(i,r,l) for(int i=r; i>=l; --i)
#define pii pair<int,int>
#define mp make_pair
#define pb push_back 

using namespace std;

const int N=100005;

int Pluto, n, m, q, B, tot, o[N], x[N], y[N], id[N], tag[N], len, Ans[N], vis[N], X[N], Y[N];
pii u[N];
map<pii,int> sav;
vector<int> arr[N], to[N];
struct DSU {
	int dsu[N], ans;
	void clear() { ans=0; up(i,1,n) dsu[i]=i; }
	int get(int x) { return x==dsu[x]?x:dsu[x]=get(dsu[x]); }
	void merge(int x,int y) { /*cout << "Insert " << x << ' ' << y << '\n';*/ x=get(x), y=get(y); if(x!=y) dsu[x]=y, ++ans; }
	void dfs(int x) { for(int y:to[x]) if(!vis[y]) vis[y]=1, dfs(y); }
	int query(vector<int> e,int lim) {
		int num=0, ret=0;
		vector<pii> re;
		for(int u:e) {
			int l=X[u], r=Y[u];
			if(lim>0&&(l>lim||r>lim)) continue;
			if(lim<0&&(l<=-lim||r<=-lim)) continue;
			l=get(l), r=get(r);
			if(l!=r) {
				if(!to[l].size()) ++num;
				if(!to[r].size()) ++num;
				to[l].pb(r), to[r].pb(l), re.pb(mp(l,r));;
			}
		}
//		cout << "fuck " << lim << " : "; for(pii u:re) cout << '(' << u.first << ',' << u.second << ") "; cout << '\n';
		for(pii u:re) {
			int l=u.first, r=u.second;
			if(!vis[l]) ++ret, vis[l]=1, dfs(l);
			if(!vis[r]) ++ret, vis[r]=1, dfs(r);
		}
		for(pii u:re) {
			int l=u.first, r=u.second;
			vis[l]=vis[r]=0, to[l].clear(), to[r].clear();
		}
		return num-ret;
	}
//	void out() { cout << "dsu : "; up(i,1,n) cout << get(i) << ' '; cout << '\n'; }
} dsu;
struct QUR { int id, d, p; } xun[N], cha[N];


signed main() {
//	freopen("wa.in","r",stdin);
	freopen("connect.in","r",stdin);
	freopen("connect.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0);
	cin >> Pluto >> n >> m >> q, B=sqrt(m); // 记得改回来w 
	up(i,1,m) {
		cin >> o[i] >> x[i] >> y[i];
		if(y[i]<x[i]) swap(x[i],y[i]);
		if(o[i]==0) id[i]=sav[mp(x[i],y[i])]=++tot, X[tot]=x[i], Y[tot]=y[i];
		if(o[i]==1) id[i]=sav[mp(x[i],y[i])];
	} sav.clear();
	up(i,1,q) xun[i].id=i, cin >> xun[i].d >> xun[i].p;
	sort(xun+1,xun+1+q,[](QUR A,QUR B){return A.d<B.d;});
	int pos=1;
	for(int l=1, r=B; l<=m; l=r+1, r=min(l+B-1,m)) {
		// 操作求解 l~r
		
//		cout << "Round " << l << ' ' << r << '\n';
		
		memset(tag,0,sizeof(tag));
		up(i,1,l-1) {
			if(o[i]==0) tag[id[i]]=1;
			if(o[i]==1) tag[id[i]]=0;
		}
		
		up(i,l,r) if(o[i]==1&&tag[id[i]]) tag[id[i]]=0, arr[l-1].pb(id[i]);
		up(i,l,r) {
			bool flag=1;
			for(int u:arr[i-1]) if(u==id[i]) flag=0; else arr[i].pb(u);
			if(flag) arr[i].pb(id[i]);
		} // 最后记得 clear
		
		len=0;
		up(i,1,m) if(tag[i]) u[++len]=mp(X[i],Y[i]);
//		up(i,1,len) cout << '(' << u[i].first << ',' << u[i].second << ") "; cout << '\n';
		
		int Q=0, j;
		while(pos<=q&&l<=xun[pos].d&&xun[pos].d<=r) cha[++Q]=xun[pos++];
		sort(cha+1,cha+1+Q,[](QUR A,QUR B){return A.p<B.p;});
		
		sort(u+1,u+1+len,[](pii A,pii B){return A.second<B.second;});
		dsu.clear(), j=1;
		up(i,1,Q) {
			int d=cha[i].d, p=cha[i].p;
			while(j<=len&&u[j].second<=p) dsu.merge(u[j].first,u[j].second), ++j;
//			cout << "cha " << p << " : "; for(int f:arr[d]) cout << '(' << X[f] << ',' << Y[f] << ") "; cout << '\n';
//			dsu.out();
			Ans[cha[i].id]+=p-dsu.ans-dsu.query(arr[d],p);
//			cout << "contribute = " << p-dsu.ans-dsu.query(arr[d],p) << '\n';
		}
		
		sort(u+1,u+1+len,[](pii A,pii B){return A.first >B.first ;});
		dsu.clear(), j=1;
		dn(i,Q,1) {
			int d=cha[i].d, p=cha[i].p;
			while(j<=len&&u[j].first>p) dsu.merge(u[j].first,u[j].second), ++j;
//			cout << "Cha " << p << " : "; for(int f:arr[d]) cout << '(' << X[f] << ',' << Y[f] << ") "; cout << '\n';
//			dsu.out();
			Ans[cha[i].id]+=n-p-dsu.ans-dsu.query(arr[d],-p);
//			cout << "contribute = " << n-p << ' ' << -dsu.ans << ' ' << -dsu.query(arr[d],-p) << '\n';
		}
		
		up(i,l-1,r) arr[i].clear();
	}
	up(i,1,q) cout << Ans[i]-1 << '\n';
	return 0;
}
posted @ 2025-10-14 19:17  Hypoxia571  阅读(10)  评论(0)    收藏  举报