n
i
b
o
R

2026CSP-S模拟3 题解

T1 不相邻集合

首先,将出现的数存进桶里,如果出现的数是连续的,那么我们选的数一定是从这个连续段的第一个数开始,每隔一个数选一个,那么这个连续段的答案是 \(\lceil \frac{n}{2} \rceil\)

对于所有情况,出现的数一定是若干连续段,每段贡献都是上述方法求得的,下面只需要考虑新插入一个数对连续段的影响。


1.新建一段。

2.加入一个连续段的开头或者结尾。

3.将两个连续段拼接起来。


分情况维护即可。

code

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,ans;
int cnt[N];
struct Node
{
	int l,r,res;
} node[N];
int main() {
	freopen("set.in","r",stdin);
	freopen("set.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		if(cnt[x])
		{
			cout<<ans<<' ';
			continue;
		}
		cnt[x]++;
		if(!cnt[x-1] && !cnt[x+1])
		{
			ans++;
			node[x].l=node[x].r=x;
			node[x].res=1;
			cout<<ans<<' ';
			continue;
		}
		if(cnt[x-1] && cnt[x+1])
		{
			ans-=node[x-1].res;
			ans-=node[x+1].res;
			int L=node[x-1].l,R=node[x+1].r;
			node[L].r=R;
			node[R].l=L;
			int now=(R-L)+1;
			ans+=ceil(now/2.0);
			node[L].res=node[R].res=ceil(now/2.0);
			cout<<ans<<' ';
			continue;
		}
		if(cnt[x-1])
		{
			ans-=node[x-1].res;
			int L=node[x-1].l,R=x;
			node[L].r=R;
			node[R].l=L;
			int now=(R-L)+1;
			ans+=ceil(now/2.0);
			node[L].res=node[R].res=ceil(now/2.0);
			cout<<ans<<' ';
			continue;
		}
		if(cnt[x+1])
		{
			ans-=node[x+1].res;
			int L=x,R=node[x+1].r;
			node[L].r=R;
			node[R].l=L;
			int now=(R-L)+1;
			ans+=ceil(now/2.0);
			node[L].res=node[R].res=ceil(now/2.0);
			cout<<ans<<' ';
			continue;
		}
	}
}

T2 线段树

在线段树中找到所有维护区间满足 $ x \le l \le r \le y$ 的节点是简单的,观察到,答案是所有这样节点的子树编号和的和。

考虑如何求出一棵维护的区间长度为 \(k\),根节点编号为 \(x\) 的线段树的编号和,如果可以求解,那么上面的问题是可解的。

设上面的答案为 \(f(x,k)\)

\(f(x,k)=f(2x,\lceil \frac{x}{2} \rceil) + f(2x + 1,\lfloor \frac{x}{2} \rfloor) + 4x + 1\)

上述等式满足一次函数性质,则必然有:

\[f(x,k)=ax+b \]

其中 $b=f(0,k) $ ,\(a=f(1,k)-f(0,k)\)

如果直接记忆化搜索能不能过呢?

分析可知,对于一颗线段树同层的节点,至少有三个 \(k\) 相同的节点,对于 \(k\) 相同的节点,其满足的函数 \(ax+b\) 也相同,可以直接带入 \(x\),综上,一棵有 \(n\) 个节点的线段树,最坏情况下需要递归 $ \frac{n\log_{2}{n}}{2^{\log_{2}{n}}} $ 次,复杂度为 \(O(\log^2{n})\)

code

#include<bits/stdc++.h>
#define lid (id<<1)
#define rid (id<<1 | 1)
#define int long long
using namespace std;
const int mod=1e9+7;
map<int,int> resa,resb;
int n,x,y;
int ans;

int calc(int id,int k)
{
	if(k==1) return id;
	if(!id)
	{
		if(resb[k]) return resb[k];
		int now=(calc(1,k/2)+calc(0,(k+1)>>1))%mod;
		resb[k]=now;
		return now;
	}
	if(id==1)
	{
		if(resa[k]) return resa[k];
		int now=(calc(3,k/2)+calc(2,(k+1)>>1)+1)%mod;
		resa[k]=now;
		return now;
	}
	int a,b;
	if(resa[k]) a=resa[k];
	else a=calc(1,k);
	if(resb[k]) b=resb[k];
	else b=calc(0,k);
	return (id%mod*((a-b)%mod+mod)%mod+b)%mod;
}

void build(int id,int l,int r)
{
	if(l>y || r<x) return;
	if(l>=x && r<=y)
	{
		ans+=calc(id,r-l+1);
		ans%=mod;
		return;
	}
	int mid=l+r>>1;
	build(lid,l,mid),build(rid,mid+1,r);
}

void sol()
{
	ans=0;
	cin>>n>>x>>y;
	build(1,1,n);
	cout<<ans<<'\n';
}
signed main() {
	freopen("segt.in","r",stdin),freopen("segt.out","w",stdout);
	int T;
	cin>>T;
	while(T--) sol();
}

T3 魔法师

题目中给出的式子没办法整体维护,只能考虑分讨。
设魔杖集合为 \(S\),魔咒集合为 \(M\)

假设 \(a_s+a_m>b_s+b_m\),整理可得 \(b_m-a_m<a_s-b_s\)

\(v=b_m-a_m\)\(u=a_s-b_s\)

那么所有 \(u>v\) 的节点的贡献是来自 \(a\) 的。考虑线段树分治:每次钦定 \(u<v\)\(u\) 来自左子树,\(v\) 来自右子树,计算 \(a\) 的答案;钦定 \(u>v\)\(u\) 来自右子树, \(v\) 来自左子树,计算 \(b\) 的答案。然后再分别递归处理 \(u,v\) 同侧的情况即可。

线段树需要维护区间最小的 $ a_s, a_m, b_s , b_m $ 每次单点修改,线段树分治。由于每个叶子节点对应的值(即 \(u\)\(v\))有多个,所以需要每个叶子节点上维护 4 个 set,分别维护存在的值中最小的上述信息。

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

code

#include<bits/stdc++.h>
#define lx (x<<1)
#define rx (x<<1 | 1)
#define int long long
using namespace std;

const int N=5e5+5,inf=0x3f3f3f3f,eps=250001;
struct Data
{
	int as,bs,am,bm;
	int res;
};
struct node
{
	int l,r;
	Data data;
} tree[N<<2];
int lst;
int q;

multiset<pair<int,int>> g1[N][2];
multiset<pair<int,int>> g2[N][2];
void pushup(int x)
{
	tree[x].data.am=min(tree[lx].data.am,tree[rx].data.am);
	tree[x].data.bm=min(tree[lx].data.bm,tree[rx].data.bm);
	tree[x].data.as=min(tree[lx].data.as,tree[rx].data.as);
	tree[x].data.bs=min(tree[lx].data.bs,tree[rx].data.bs);
	int k1,k2;
	k1=tree[lx].data.am+tree[rx].data.as;
	k2=tree[lx].data.bs+tree[rx].data.bm;
	tree[x].data.res=min(k1,k2);
	tree[x].data.res=min(tree[x].data.res,tree[lx].data.res);
	tree[x].data.res=min(tree[x].data.res,tree[rx].data.res);
}

void build(int x,int l,int r)
{
	tree[x].l=l,tree[x].r=r;
	if(l==r)
	{
		tree[x].data={inf,inf,inf,inf,inf};
		return;
	}
	int mid=l+r>>1;
	build(lx,l,mid);
	build(rx,mid+1,r);
	pushup(x);
}

void ins(int x,pair<int,int> d,int type,int id)
{
	int l=tree[x].l,r=tree[x].r;
	if(l==r)
	{
		if(type)
		{
			tree[x].data.am=d.first;
			tree[x].data.bm=d.second;
		}
		else
		{
			tree[x].data.as=d.first;
			tree[x].data.bs=d.second;
		}
		int k1=tree[x].data.am+tree[x].data.as;
		int k2=tree[x].data.bm+tree[x].data.bs;
		tree[x].data.res=min(k1,k2);
		return;
	}
	int mid=l+r>>1;
	if(id<=mid) ins(lx,d,type,id);
	else ins(rx,d,type,id);
	pushup(x);
}

signed main() {
	freopen("magic.in","r",stdin),freopen("magic.out","w",stdout);
	int c;
	cin>>q>>c;
	build(1,1,N-3);
	for(int i=1;i<=q;i++)
	{
		int opt,t,a,b;
		cin>>opt>>t>>a>>b;
		if(c) a^=lst,b^=lst;
		if(opt==1)
		{
			if(t==0)
			{
				int x=a-b+eps;
				g1[x][0].insert({a,b});
				g2[x][0].insert({b,a});
				a=(*(g1[x][0].begin())).first;
				b=(*(g2[x][0].begin())).first;
				ins(1,{a,b},0,x);
			}
			if(t==1)
			{
				int x=b-a+eps;
				g1[x][1].insert({a,b});
				g2[x][1].insert({b,a});
				a=(*(g1[x][1].begin())).first;
				b=(*(g2[x][1].begin())).first;
				ins(1,{a,b},1,x);
			}
		}
		if(opt==2)
		{
			if(t==0)
			{
				int x=a-b+eps;
				g1[x][0].erase({a,b});
				g2[x][0].erase({b,a});
				if(g1[x][0].size())
				{
					a=(*(g1[x][0].begin())).first;
					b=(*(g2[x][0].begin())).first;
					ins(1,{a,b},0,x);
				} else {
					ins(1,{inf,inf},0,x);
				}
			}
			if(t==1)
			{
				int x=b-a+eps;
				g1[x][1].erase({a,b});
				g2[x][1].erase({b,a});
				if(g1[x][1].size())
				{
					a=(*(g1[x][1].begin())).first;
					b=(*(g2[x][1].begin())).first;
					ins(1,{a,b},1,x);
				} else {
					ins(1,{inf,inf},1,x);
				}
			}
		}
		lst=tree[1].data.res;
		if(lst==0x3f3f3f3f) lst=0;
		std::cout<<lst<<'\n';
	}
}

T4 园艺

斜率优化DP

不想写,咕咕咕。

posted @ 2026-06-21 14:14  TTC_84  阅读(30)  评论(4)    收藏  举报