首页 > 其他分享 >CSP 模拟 29

CSP 模拟 29

时间:2024-09-08 20:25:33浏览次数:13  
标签:std insert ch int res 29 long CSP 模拟

T1 不相邻集合

做法一:设 \(f_i\) 表示到当前位置以 \(i\) 结尾的最长长度,\(s_i\) 表示以 \(i\) 开头的最长长度。有 \(a>b\Leftrightarrow f_a>f_b\Leftrightarrow s_a<s_b\),有了这个性质就可以直接上线段树维护,时间复杂度 \(\mathcal{O}(n\log n)\)。

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=5e5+100,LEN=5e5+10;
int n,a[N];
struct TREE{int mx;}t[N<<2],f[N<<2];
inline void insert(int p,int l,int r,int pos,int x){
	if(l==r){t[p].mx=x;return ;}
	int mid=l+r>>1;
	if(pos<=mid)insert(p<<1,l,mid,pos,x);
	else insert(p<<1|1,mid+1,r,pos,x);
	t[p].mx=std::max(t[p<<1].mx,t[p<<1|1].mx);
}
inline int query(int p,int l,int r,int L,int R){
	if(L>R)return 0;
	if(l>=L&&r<=R)return t[p].mx;
	int res=0,mid=l+r>>1;
	if(L<=mid)res=query(p<<1,l,mid,L,R);
	if(R>mid)res=std::max(res,query(p<<1|1,mid+1,r,L,R));
	return res;
}
inline void insert1(int p,int l,int r,int pos,int x){
	if(l==r){f[p].mx=x;return ;}
	int mid=l+r>>1;
	if(pos<=mid)insert1(p<<1,l,mid,pos,x);
	else insert1(p<<1|1,mid+1,r,pos,x);
	f[p].mx=std::max(f[p<<1].mx,f[p<<1|1].mx);
}
inline int query1(int p,int l,int r,int L,int R){
	if(L>R)return 0;
	if(l>=L&&r<=R)return f[p].mx;
	int res=0,mid=l+r>>1;
	if(L<=mid)res=query1(p<<1,l,mid,L,R);
	if(R>mid)res=std::max(res,query1(p<<1|1,mid+1,r,L,R));
	return res;
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();for(int i=1;i<=n;++i)a[i]=read();
	for(int i=1;i<=n;++i){
		int x=query(1,1,LEN,1,a[i]-2)+1;
		insert(1,1,LEN,a[i],x);
		x=query1(1,1,LEN,a[i]+2,LEN)+1;
		insert1(1,1,LEN,a[i],x);
		std::cout<<std::max(f[1].mx,t[1].mx)<<' ';
	}
}

做法二:对于一个长度为 \(len\) 的极长连续段,答案为 \(\left\lceil \frac{len}{2}\right\rceil\),然后不连续的段一定可以接在一起,所以对于一个序列,它的答案为 \(\sum \left\lceil \frac{len_i}{2}\right\rceil\),可以用并查集维护连续段。

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=5e5+10;
int n,a[N],fa[N],size[N];
bool mp[N];
inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline void merge(int x,int y){
	x=find(x),y=find(y);
	if(size[x]>size[y])std::swap(x,y);
	fa[x]=y;size[y]+=size[x];
}
inline int c(int x){return (x-1)/2+1;}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();for(int i=1;i<=n;++i)a[i]=read();
	int ans=0;
	for(int i=1;i<=5e5+5;++i)fa[i]=i,size[i]=1;
	for(int i=1;i<=n;++i){
		if(mp[a[i]]){std::cout<<ans<<' ';continue;}
		else mp[a[i]]=1;
		if(!mp[a[i]-1]&&!mp[a[i]+1])ans++;
		if(mp[a[i]-1]&&!mp[a[i]+1]){
			int sum1=size[find(a[i]-1)];
			merge(a[i]-1,a[i]);
			ans=ans-c(sum1)+c(sum1+1);
		}
		if(mp[a[i]+1]&&!mp[a[i]-1]){
			int sum1=size[find(a[i]+1)];
			merge(a[i]+1,a[i]);
			ans=ans-c(sum1)+c(sum1+1);
		}
		if(mp[a[i]+1]&&mp[a[i]-1]){
			int sum1=size[find(a[i]+1)],sum2=size[find(a[i]-1)];
			int sum=sum1+sum2+1;
			merge(a[i],a[i]+1),merge(a[i],a[i]-1);
			ans=ans-c(sum1)-c(sum2)+c(sum);
		}
		std::cout<<ans<<' ';
	}
}

T2 线段树

观察后发现,对于一个长度为 \(len\),编号为 \(p\) 的节点,它的贡献为 \(k\cdot p+b\),一次函数。\(k_{len}=2k_{mid-l+1}+2k_{r-mid},b_{len}=b_{mid-l+1}+b_{r-mid}+k_{r-mid}\) 记忆化搜索即可。

#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
std::unordered_map<int,std::pair<int,int>> mp;
const int mod=1e9+7;
inline int mo(int x){return x<mod?x:x%mod;}
int T,n,x,y;
inline std::pair<int,int> work(int l,int r,int x,int y){
	if(l>=x&&r<=y){
		auto it=mp[r-l+1];
		if(it.first){return it;}
		int mid=l+r>>1;
		auto ls=work(l,mid,x,y),rs=work(mid+1,r,x,y);
		return mp[r-l+1]={mo(2*(ls.first+rs.first)+1),mo(ls.second+rs.first+rs.second)};
	}
	int mid=l+r>>1;std::pair<int,int> res={0,0};
	if(x<=mid){auto ls=work(l,mid,x,y);res.first=mo(res.first+ls.first*2),res.second=mo(res.second+ls.second);}
	if(y>mid){auto rs=work(mid+1,r,x,y);res.first=mo(res.first+rs.first*2),res.second=mo(res.second+rs.first+rs.second);}
	return res;
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	T=read();mp[1]={1,0};
	while(T--){
		n=read(),x=read(),y=read();
		auto it=work(1,n,x,y);
		std::cout<<mo(it.first+it.second)<<'\n';
	}
}

T3 魔法师

小清新线段树题。
由于不知道答案是由谁贡献,所以很难来解决问题,考虑简单推下式子,对于法杖 \(p\) 和咒语 \(q\) 结合,当 \(a_p+a_q<b_p+b_q\) 时,有 \(a_p-b_p<b_q-a_q\),设 \(u_p=a_p-b_p,u_q=b_q-a_q\),这样根据 \(u_p\) 和 \(u_q\) 的大小关系,就可以确定答案由 \(a\) 贡献还是由 \(b\) 贡献,然后就能确定去最小的 \(a\) 还是最小的 \(b\),此时以 \(u\) 为位置,用线段树维护区间最小的 \(a_p,a_q,b_p,b_q,ans\) 即可。修改使用 multiset 即可。
上传时 \(ans\) 用左区间的 \(b_p\) 加右区间的 \(b_q\) 和左区间的 \(a_q\) 加右区间的 \(a_p\) 来更新即可,(保证 \(u_p\) 和 \(u_q\) 的大小关系)。

#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2.5e5+10,LEN=5e5+5,P=2.5e5+1,inf=1e9;
int n,T,lastans,cnt;
std::multiset<int> sap[N],sbp[N],saq[N],sbq[N];
inline std::pair<int,int> get(std::pair<int,int> a,std::pair<int,int> b){return {std::min(a.first,b.first),std::min(a.second,b.second)};}
struct TREE{int ap,bp,aq,bq,res,id;}t[LEN<<2];
inline void update(int p){
	t[p]={std::min(t[ls].ap,t[rs].ap),std::min(t[ls].bp,t[rs].bp),std::min(t[ls].aq,t[rs].aq),std::min(t[ls].bq,t[rs].bq),inf,0};
	t[p].res=std::min(t[ls].res,t[rs].res);
	t[p].res=std::min(t[p].res,std::min(t[ls].bp+t[rs].bq,t[rs].ap+t[ls].aq));
}
inline void build(int p,int l,int r){
	t[p]={inf,inf,inf,inf,inf,0};
	if(l==r)return ;
	int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);
}
inline void change(int p,int l,int r,int pos,int a,int b,int pd,int flag){
	if(l==r){
		if(flag){
			if(!t[p].id)t[p].id=++cnt,saq[cnt].insert(inf),sbq[cnt].insert(inf),sap[cnt].insert(inf),sbp[cnt].insert(inf);int x=t[p].id;
			if(pd)saq[x].insert(a),sbq[x].insert(b);else{if(saq[x].find(a)!=saq[x].end()&&sbq[x].find(b)!=sbq[x].end())saq[x].erase(a),sbq[x].erase(b);}
			t[p].aq=*saq[x].begin(),t[p].bq=*sbq[x].begin();
		}else{
			if(!t[p].id)t[p].id=++cnt,saq[cnt].insert(inf),sbq[cnt].insert(inf),sap[cnt].insert(inf),sbp[cnt].insert(inf);int x=t[p].id;
			if(pd)sap[x].insert(a),sbp[x].insert(b);else{if(sap[x].find(a)!=sap[x].end()&&sbp[x].find(b)!=sbp[x].end())sap[x].erase(a),sbp[x].erase(b);}
			t[p].ap=*sap[x].begin(),t[p].bp=*sbp[x].begin();
		}
		t[p].res=t[p].bp+t[p].bq;
		return;
	}
	int mid=l+r>>1;
	if(pos<=mid)change(ls,l,mid,pos,a,b,pd,flag);
	else change(rs,mid+1,r,pos,a,b,pd,flag);
	update(p);
}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();T=read();build(1,1,LEN);
	for(int i=1;i<=n;++i){
		int op=read(),tt=read(),a=read(),b=read();
		if(T)a^=lastans,b^=lastans;
		if(op==2){op=0;}
		if(tt)change(1,1,LEN,b-a+P,a,b,op,tt);else change(1,1,LEN,a-b+P,a,b,op,tt);
		lastans=t[1].res;
		if(lastans>=inf)lastans=0;std::cout<<lastans<<'\n';
	}
}

T4 园艺

神秘题,\(f_{i,j,0/1}\) 表示拔草区间在 \([i,j]\),左右端点时,拔草长度和未拔草长度的最小值,复杂度 \(\mathcal{O}(n^2)\),没啥可优化的地方了。
发现对于每棵草,它的贡献就是到它之前走过的距离,设 \(f_i\) 表示到达 \(i\) 位置时,剩下所有草的长度加上所有草到当前位置的距离的最小值。靠近一棵草时,它的贡献不变,远离一棵草时,它会增加远离距离乘上 \(2\) 的贡献,设 \(i<j\),\(s\) 表示前缀和,有

\[\begin{aligned} &f_i=f_j+2(n-j)(s_j-s_i)\\ &f_j=f_i+2(i-1)(s_j-s_i) \end{aligned} \]

需要来模拟 dij 来转移,由于拔草区间为 \([l,r],l\le k\le r\),且从 \(k\) 往两边延伸,\(f_i\) 递增。所以区间 \([l,r]\) 中的 \(f\) 值是松弛好的,每次需要更新的点为 \(f_{l-1},f_{r+1}\),更新两者之后中的较小值为松弛好的点。这样省去了找到最小值的 \(\mathcal{O}(n)\),但是转移的复杂度没变,简单推下转移式子,发现是斜率优化,可以做到均摊 \(\mathcal{O}(1)\) 转移。整体时间复杂度为 \(\mathcal{O}(n)\)。

#include<bits/stdc++.h>
#define int __int128
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=2e6+10,inf=9e18;
int f[N],d[N],s[N],n,k,L,R,qi[N],headi=1,taili,qj[N],headj=1,tailj;
bool vis[N];
inline int Abs(int i){return i<0?-i:i;}
inline int Yi(int i){return f[i]-2*(i-1)*s[i];}
inline int Yj(int j){return f[j]+2*(n-j)*s[j];}
inline int calcj(int i,int j){return f[i]+2*(i-1)*(s[j]-s[i]);}
inline int calci(int j,int i){return f[j]+2*(n-j)*(s[j]-s[i]);}
signed main(){
	// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
	std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
	n=read();k=read();for(int i=2;i<=n;++i)d[i]=read(),s[i]=s[i-1]+d[i];
	memset(f,0x3f,sizeof(f));f[k]=0;for(int i=1;i<=n;++i)f[k]+=Abs(s[k]-s[i]);
	std::queue<int> q;q.push(k);L=k,R=k;
	while(!q.empty()){
		int x=q.front();q.pop();
		L=std::min(L,x);R=std::max(R,x);
		int i=L-1,j=R+1;
		if(L==1||R==n)break;
		if(x<=k){while(headi<taili&&(Yi(qi[taili])-Yi(x))*(qi[taili-1]-qi[taili])>=(Yi(qi[taili-1])-Yi(qi[taili]))*(qi[taili]-x))taili--;qi[++taili]=x;}
		while(headi<taili&&calcj(qi[headi],j)>=calcj(qi[headi+1],j))headi++;f[j]=calcj(qi[headi],j);
		if(x>=k){while(headj<tailj&&(Yj(x)-Yj(qj[tailj]))*(qj[tailj]-qj[tailj-1])<=(Yj(qj[tailj])-Yj(qj[tailj-1]))*(x-qj[tailj]))tailj--;qj[++tailj]=x;}
		while(headj<tailj&&calci(qj[headj],i)>=calci(qj[headj+1],i))headj++;f[i]=calci(qj[headj],i);
		q.push(f[i]>f[j]?j:i);
	}
	std::cout<<(ll)std::min(f[1],f[n])<<'\n';
}

总结

T1 没签上到是我 shaber,后面暴力没打好是我 shaber,T3 set 没开好调半天是我 shaber,T4 压行把句子写 if 外面了是我 shaber,总结:我就是个 shaber。

标签:std,insert,ch,int,res,29,long,CSP,模拟
From: https://www.cnblogs.com/Ishar-zdl/p/18403376

相关文章

  • csp-s模拟2
    A.不相邻集合可以发现,一个数只有在第一次出现才会做贡献,对于一个连续数段\(1,2,3...n\),它最多提供\(\lceil\frac{n}{2}\rceil\)的贡献,所以只需要维护极长连续段即可点击查看代码#include<bits/stdc++.h>constintmaxn=3e5+10;usingnamespacestd;intn,a[maxn],f......
  • 9.8 模拟赛(炼石计划 11 月 11日 CSP-S 十连测 #9)
    炼石计划11月11日CSP-S十连测#9【补题】-比赛-梦熊联盟(mna.wang)\(100+60+20+15=195\)。复盘顺序开题。T1是二分板子。写之前思考好了所有代码细节直接写代码。一遍过了所有大样例。T2。题意好麻烦。\(35\)分是极易的。跳过\(25\)分部分分,直接想正解。有......
  • csp模拟2
    T1原题根据倒数第二,三个部分分的提示,我们可以发现一个性质,如果两个连续的序列中间被间隔开,如\(1,2,3,4,6,7,8,9\)那这两个序列中选数操作互不影响,那这就比较好办了,一个长度为\(n\)连续序列最多可以选出$\lceil\frac{n}{2}\rceil$个数,那我们只需要维护连续的区间的个......
  • NOIP 模拟赛 Round5
    T1:赛时一眼秒了,然后爆单了。没有什么思路就要想到一些套路比如把模拆成减除,然后发现有个\(k\),自然思路就出来了,\(k\)必然是一个数的因数。复杂度是根号的。注意特判\(s=0,s<0\)!!!T2:一眼二分贪心……显然不能优化建图按照a排序也是显然的。T3:最唐的地方是所有人都在考虑......
  • csp-s模拟1
    A.喜剧的迷人之处在于切入点在\(a\),考虑\(a\)是不是完全平方数,是的话直接找最小能匹配的完全平方数即可,不是的话\(a\)一定可以表示成\(kx^2\)的形式,倒着找到最大的平方因子除去,只需要在\(L\)~\(R\)间找到一个最小的数也等于\(kx^2\)即可点击查看代码#include<bits......
  • ZR 2024 NOIP 十连 & CSP 七连
    NOIPday1T1简单建图跑bfs,vector会被卡空间,用前向星才能过。T2注意到原串是否确定不重要,因为无非是把每种可能的转移都多做一遍。把所有可能出现的回文串的一半插进AC自动机中,就可以转移了。CSPday1T3设\(nxt_i\)表示下一个与\(a_i\)值相同的位置到\(i\)的距......
  • P2926 [USACO08DEC] Patting Heads S
    根据题意我们不难想出枚举1到n,然后逐个判断能整除自己的个数,时间复杂度O(N^2),n<=1e5,明显会爆炸。考虑优化,我们看到a[i]<=1e6,可以开一个桶来记录,然后枚举1到maxn(即最大的a[i]),然后从i开始,每次加i,w[j](记录能整除j的a[i]的个数)+=c[i](值为i的个数)。代码:#include <bits/stdc......
  • C++STL之stack和queue容器适配器:基本使用及模拟实现
    目录stack的介绍和使用stack的介绍stack的使用queue的介绍和使用queue的介绍queue的使用priority_queue的介绍和使用priority_queue的介绍priority_queue的使用deque双端队列(容器)deque的介绍及使用deque的缺点deque的原理(了解)容器适配器概念stack和queue的......
  • NOIP2024模拟赛5 总结
    NOIP2024模拟赛5总结T1天才俱乐部特判了\(sum-s<0\),但没有考虑\(sum-s=0\)。挂为0pts。T2实战教学由于写的不够优,贪心+二分的思路TLE了。由于不明原因,输出\(\max(a_i+b_i)\)能过。非常神奇。T3穿越银匙之门T4绳网委托一句话总结:挂分挂成sb了。......
  • STM32 PWM 详解(基于 STM32F429 HAL 库)
    目录前言一、PWM简介二、STM32F429的PWM功能1.定时器资源2.PWM模式3.PWM原理图三、使用HAL库配置STM32F429的PWM1.开启时钟2.配置定时器3.配置通道 4.启动定时器 5.PWM占空比的调节 四、应用实例五、总结前言        在嵌入式系统开......