csp-s模擬2

_君の名は發表於2024-09-08

A. 不相鄰集合

可以發現,一個數只有在第一次出現才會做貢獻,對於一個連續數段 \(1,2,3...n\) ,它最多提供 \(\lceil \frac{n}{2} \rceil\)的貢獻,所以只需要維護

極長連續段即可

點選檢視程式碼
#include<bits/stdc++.h>
const int maxn=3e5+10; 
using namespace std;
int n,a[maxn],fa[500005],size[500005],ans;
bool vis[500005]; 
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int mo(int x){return (size[x]+1)>>1;}
void merge(int x,int y)
{
	int a=find(x),b=find(y); 
	if(a==b) return ;
	fa[b]=a;
	ans-=mo(a)+mo(b);
	size[a]+=size[b];
	ans+=mo(a);
}

int main()
{
//	freopen("set1.in","r",stdin);
//	freopen("T.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	
	cin>>n;
	for(int i=1;i<=500001;i++) fa[i]=i,size[i]=1;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(!vis[a[i]])ans++; 
		vis[a[i]]=1;
		if(vis[a[i]-1])
		{
//			cout<<find(a[i]-1)<<" "<<find(a[i])<<endl; 
			merge(a[i]-1,a[i]);
		}
		if(vis[a[i]+1])
		{
			merge(a[i],a[i]+1);
		}
		cout<<ans<<" ";
	}
	

	return 0;
}
/*

*/

B. 線段樹

\(f(n,x)\) 為有 \(n\) 個節點,根標號為 \(x\) 的線段樹的節點標號和,可以發現其是一個確定的一次函式

因為 \(f(n,x)=f(\lceil \frac{n}{2} \rceil,2x)+f(\lfloor \frac{n}{2} \rfloor,2x+1)+x\),可以推出 \(k_n=2(k_{\lceil \frac{n}{2} \rceil}+k_{\lfloor \frac{n}{2} \rfloor})+1\),\(b_n=(b_{\lceil \frac{n}{2} \rceil}+b_{\lfloor \frac{n}{2} \rfloor}+k_{\lfloor \frac{n}{2} \rfloor})\)

每次查到一個區間屬於 \(x\)~\(y\) ,沒查過則處理一下 \(k_n\)\(b_n\) ,查過則直接返回,單次複雜度 \(O(log_n)\)

點選檢視程式碼
#include<bits/stdc++.h>
#define int long long 
const int mod=1e9+7;
const int maxn=2e5+10;
using namespace std;
int t,n,x,y;
map<int ,int >k,b;
void lsx(int x)
{
	if(x==1) return ;
	if(k[x]&&b[x]) return ; 
	lsx(floor(1.0*x/2));
	k[x]+=2*k[floor(1.0*x/2)];
	b[x]+=b[floor(1.0*x/2)]+k[floor(1.0*x/2)]; 
	k[x]%=mod;
	b[x]%=mod;
	lsx(ceil(1.0*x/2)); 
	k[x]+=2*k[ceil(1.0*x/2)]+1;
	b[x]+=b[ceil(1.0*x/2)];
	k[x]%=mod;
	b[x]%=mod;
}

int solve(int id,int l,int r,int x,int y)
{
	if(l>=x&&r<=y)
	{
		lsx(r-l+1);
		return (k[r-l+1]*id%mod+b[r-l+1])%mod;
	}
	int mid=l+r>>1,tem=0;
	if(mid>=x) tem=(tem+solve((id<<1)%mod,l,mid,x,y))%mod;
	if(mid<y) tem=(tem+solve((id<<1|1)%mod,mid+1,r,x,y))%mod;
	return tem;
}

signed main()
{
//	freopen("T.in","r",stdin);
//	freopen("T.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	k[1]=1,b[1]=0;
	cin>>t;
	while(t--)
	{
		cin>>n>>x>>y;
		cout<<solve(1,1,n,x,y)<<'\n';
	}

	return 0;
}
/*

*/

C. 魔法師

對於魔杖 \(p\) 和魔法 \(q\) ,它們的魔法是 \(\max(a_p+a_q,b_p+b_q)\),先分討簡化,假設 \(a_p+a_q<b_p+b_q\),則有 \(a_p-b_p<b_q-a_q\)

\(u_p=a_p-b_q,v_q=b_q-a_q\),則魔力的大小則取決於 \(u_p\)\(v_q\) 的大小,把所有魔杖和魔法以其 \(u_p/v_q\) 為下標存到一棵權值

線段樹上,找右區間最小的 \(b_p\) 和左區間最小的 \(b_q\),和找右區間最小的 \(a_p\) 和左區間最小的 \(a_q\)即可

點選檢視程式碼
#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
const int maxn=25e4+10;
const int mm=25e4;
const int inf=0x7f7f7f;
using namespace std;
int Q,T,ans,last,cnt1,cnt2;
struct lsx
{
	int l,r,a[4],ans;
}m[maxn<<3],s; 
multiset<int >q[maxn<<1][4];

void build(int id,int l,int r)
{
	m[id].l=l,m[id].r=r;
	m[id].ans=m[id].a[0]=m[id].a[1]=m[id].a[2]=m[id].a[3]=inf;
	if(l==r) return ;
	int mid=l+r>>1;
	build(lid,l,mid);
	build(rid,mid+1,r); 
}

void up(int id)
{
	for(int i=0;i<4;i++) m[id].a[i]=min(m[lid].a[i],m[rid].a[i]);
	m[id].ans=min(m[lid].a[3]+m[rid].a[1],m[lid].a[0]+m[rid].a[2]);
	m[id].ans=min(m[id].ans,min(m[lid].ans,m[rid].ans));
}

void update(int id,int x)
{
	int l=m[id].l,r=m[id].r;
	if(l==r)
	{
		m[id]=s;
		if(m[id].a[0]+m[id].a[2]<=inf||m[id].a[1]+m[id].a[3]<=inf) 
			m[id].ans=min(m[id].a[0]+m[id].a[2],m[id].a[1]+m[id].a[3]);
		return ;
	}
	int mid=l+r>>1;
	if(x<=mid) update(lid,x);
	else update(rid,x);
	up(id);
}

int main()
{
//	freopen("T.in","r",stdin);
//	freopen("T.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>Q>>T;
	build(1,1,mm*2);
	s.a[0]=s.a[1]=s.a[2]=s.a[3]=s.ans=inf;
	while(Q--)
	{
		int op,t,x,y;
		cin>>op>>t>>x>>y;
		if(T) x^=last,y^=last;
		int pos=t?y-x+mm:x-y+mm;
		if(op==1)
		{
			if(t)
			{
				q[pos][0].insert(x),q[pos][1].insert(y);
				for(int i=0;i<4;i++)
				{
					if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
					else s.a[i]=inf;
				}
//				cout<<s.a[0]<<" "<<s.a[1]<<" "<<s.a[2]<<" "<<s.a[3]<<endl;
				update(1,pos);
			}
			else
			{
				q[pos][2].insert(x),q[pos][3].insert(y);
				for(int i=0;i<4;i++)
				{
					if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
					else s.a[i]=inf;
				}
//				cout<<s.a[0]<<" "<<s.a[1]<<" "<<s.a[2]<<" "<<s.a[3]<<endl;
				update(1,pos);
			}
		}
		else
		{
			if(t)
			{
				if(q[pos][0].size()&&q[pos][1].size()&&q[pos][0].find(x)!=q[pos][0].end()&&q[pos][1].find(y)!=q[pos][1].end())
				{
					q[pos][0].erase(q[pos][0].find(x));
					q[pos][1].erase(q[pos][1].find(y));
					for(int i=0;i<4;i++)
					{
						if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
						else s.a[i]=inf;
					}
					update(1,pos);
				}
				
			}
			else
			{
				if(q[pos][2].size()&&q[pos][3].size()&&q[pos][2].find(x)!=q[pos][2].end()&&q[pos][3].find(y)!=q[pos][3].end())
				{
					q[pos][2].erase(q[pos][2].find(x));
					q[pos][3].erase(q[pos][3].find(y));
					for(int i=0;i<4;i++)
					{
						if(q[pos][i].size()) s.a[i]=*q[pos][i].begin();
						else s.a[i]=inf;
					}
					update(1,pos);
				}
				
			}
		}
		cout<<(m[1].ans>=inf?0:m[1].ans)<<'\n'; 
		last=(m[1].ans>=inf?0:m[1].ans);
	}

	return 0;
}
/*

*/

相關文章