CSP模拟<反思> (16~20)

csp模拟16#

魔法仪式#

考虑经典的分治解决区间数数题的做法,对于一个区间,取中点,计算左端点在中点左边且右端点在中点右边的方案数,之后递归分治,即可求得答案。

考虑枚举中点后怎么计算,分开考虑最大值在左边和在右边的情况,以在左边的情况为例,从中点向左枚举左端点,不断维护右端点的可选范围(只需要保证最大值在左边),同时维护数组num[k]表示当前从中点到右边余数为k的方案数,这样对于每个左端点,只要在num中查询对应的值。O(nlogn)

做过的有趣的区间问题第负一题

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3*1e5+10;
const int M=3*1e6+10;
int a[N];
int smod[M];
int tot;
int fk[M];
long long ans=0;
int n,k;
void solve(int l,int r){
	if(l==r) return;
	int mid=(l+r)/2;
	int suml=0,ma=-1,sumr=0;
	int j=mid;
	tot=0;
	for(int i=mid;i>=l;i--){
		suml+=a[i];
		ma=max(ma,a[i]);
		int op=1;
		while(j<r && a[j+1]<ma){
			j++;
			sumr+=a[j];
			smod[sumr%k]++;
			fk[++tot]=sumr%k;
		}
		ans+=smod[(k-(suml-ma)%k)%k];
	}
	for(int i=1;i<=tot;i++){
		smod[fk[i]]=0;
	}
	ma=-1,sumr=0,suml=0;
	j=mid+1;
	tot=0;
	for(int i=mid+1;i<=r;i++){
		sumr=sumr+a[i];
		ma=max(ma,a[i]);
		while(j>l && a[j-1]<=ma){
			j--;
			suml+=a[j];
			smod[suml%k]++;
			fk[++tot]=suml%k;
		}
		ans+=smod[(k-(sumr-ma)%k)%k];
	}
	for(int i=1;i<=tot;i++){
		smod[fk[i]]=0;
	}
	solve(l,mid);
	solve(mid+1,r);
}
signed main(){
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	solve(1,n);
	printf("%lld",ans);
}

这里说一下第负一题:

第负一题#

首先 n2 暴力很好想到。这又是一个区间问题,考虑分治,计算跨过中点 mid 的区间的 f 之和。

可以从区间中点向左写一个 DP,向右写一个 DPfi,0/1,0/1 表示 DPi,第一个 0/1 表示 midmid+1 是不是被选了,第二个 0/1 表示是不是选择第 i 个元素。

枚举左端点 l 对于所有右端点 r 求和:设 gi,j=max{fi,j,0,fi,j,1}
区间 [l,r] 的答案就 max{gl,0+gr,0,gl,0+gr,1,gl,1+gr,0} 但是这个式子可以写成 gl,0+max{gr,0,gr,1}gl,1+gr,0 找最大值。

如果 gr,0max{gr,0,gr,1}gl,0gl,1 这样就必须选后者。

所以将所有的 gr,0max{gr,0,gr,1} 排序,二分找到分界点用前缀和计算.

这题还是强调一个分离变量的技巧,比较大小的时候把和 l 相关的挪到一侧,和 r 相关的挪到另一侧。

具体就是先扫右侧,排序,求右侧前缀,再扫左侧,扫的过程中二分查找点,然后计算贡献。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
const int N=2*1e5+10;
int a[N];
int ans=0;
int f[N][2][2],g[N][2],cf[N],gf[N];
int sum1[N],sum2[N];
struct lhx{
	int data,id;
}d[N];
bool amp(lhx x,lhx y){
	return x.data<y.data;
}
int ask(int x,int ls,int rs){
	int l=ls,r=rs;
	while(l<r){
		int mid=(l+r+1)/2;
		if(d[mid].data<=x) l=mid;
		else r=mid-1;
	}
	return l;
}
void solve(int l,int r){
	if(l==r){
		ans=(ans+a[l])%mod;
		return;
	}
	int anss=ans;
	int mid=(l+r)/2;
	f[mid+1][0][0]=f[mid+1][0][1]=f[mid+1][1][0]=0,f[mid+1][1][1]=a[mid+1];
	g[mid+1][0]=f[mid+1][0][0];
	g[mid+1][1]=f[mid+1][1][1];
	d[mid+1].data=g[mid+1][0]-max(g[mid+1][0],g[mid+1][1]);
	d[mid+1].id=mid+1;
	for(int i=mid+2;i<=r;i++){
		f[i][0][0]=max(f[i-1][0][0],f[i-1][0][1]);
		f[i][1][0]=max(f[i-1][1][0],f[i-1][1][1]);
		f[i][0][1]=f[i-1][0][0]+a[i];
		f[i][1][1]=f[i-1][1][0]+a[i];
		g[i][0]=max(f[i][0][0],f[i][0][1]);
		g[i][1]=max(f[i][1][0],f[i][1][1]);
		d[i].data=g[i][0]-max(g[i][0],g[i][1]);
		d[i].id=i;
	}
	sort(d+mid+1,d+r+1,amp);
	sum1[mid]=sum2[mid]=sum1[r+1]=sum2[r+1]=0;
	for(int i=mid+1;i<=r+1;i++){
		sum1[i]=sum1[i-1]+g[d[i].id][0];
		sum2[i]=sum2[i-1]+max(g[d[i].id][0],g[d[i].id][1]);
	}
	f[mid][0][0]=f[mid][0][1]=f[mid][1][0]=0,f[mid][1][1]=a[mid];
	g[mid][0]=f[mid][0][0];
	g[mid][1]=f[mid][1][1];
	int j=ask(g[mid][0]-g[mid][1],mid,r);
	ans=(ans+g[mid][0]*(j-mid)%mod+sum2[j]+sum1[r]-sum1[j]+g[mid][1]*(r-j)%mod+mod)%mod;
	for(int i=mid-1;i>=l;i--){
		f[i][0][0]=max(f[i+1][0][0],f[i+1][0][1]);
		f[i][1][0]=max(f[i+1][1][0],f[i+1][1][1]);
		f[i][0][1]=f[i+1][0][0]+a[i];
		f[i][1][1]=f[i+1][1][0]+a[i];
		g[i][0]=max(f[i][0][0],f[i][0][1]);
		g[i][1]=max(f[i][1][0],f[i][1][1]);
		int j=ask(g[i][0]-g[i][1],mid,r);
		ans=(ans+g[i][0]*(j-mid)%mod+sum2[j]+sum1[r]-sum1[j]+g[i][1]*(r-j)%mod+mod)%mod;
	}
	solve(l,mid),solve(mid+1,r);
}
signed main(){
	int n;
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	solve(1,n);
	cout<<ans<<endl;
}
/*
10
850534838 749655434 745817507 991867417 645519349 373697182 427765279 182404140 260664174 366393413

5
1 3 3 2 4
*/

独特的数字#

数位状压dp,f[i][j] 表示考虑到前i位,当前选择数字的状态是j的方案数.因为要考虑选不选 0 ,所以多设一维 f[i][j][0/1] 表示有没有前导零。然后深搜的时候记忆化就行了,对于2操作直接进行二分。十六进制转化就不说了,太麻烦。

点击查看代码
__int128 solve(__int128 op,__int128 zt,bool limit,__int128 fk){
	if(!op) return 1;
	if(!limit && dp[op][zt][fk]!=-1) return dp[op][zt][fk];
	__int128 up=limit?num[op]:9;
	__int128 ans=0;
	for(__int128 i=0;i<=up;i++){
		if(fk==1 && i==0){
			ans+=solve(op-1,zt,limit&&i==up,1);
			continue;
		}
		if(zt&(1<<i)) continue;
		__int128 s=(zt|(1<<i));
		ans+=solve(op-1,s,limit&&i==up,0); 
	}
	if(!limit) dp[op][zt][fk]=ans;
	return ans;
}
__int128 ask(__int128 k){
	if(k<0) return 0;
	__int128 ans=0;
	while(k){
		num[++ans]=k%10;
		k/=10;
	}
	return solve(ans,0,1,1);
}
__int128 check(__int128 x){
	__int128 l=0,r=17647868272689243559ull;
	while(l<r){
		__int128 mid=(l+r)/2;
		if(ask(mid)>=x) r=mid;
		else l=mid+1;
	}
	if(r>=M) return -1; 
	return l;
}

csp模拟17#

选举#

可以想到莫队+线段树维护区间最大,复杂度 O(nnlogn) ,会超时,在考虑特殊性质 m<=200 可以用前缀和,rl<=200 也可过,所以这个做法甚至和 n2 暴力一样。

正解:回滚莫队里套一个分块,可以将修改变为 O(1) ,查询变为 O(n) ,总 O(nn),给党派分块,具体是维护党派的区块的最大值的数组 和 单个的个数的数组,查询直接分块查询,注意回滚删除前将两个数组赋到一个新的数组里,然后删除时再赋回来就可以了。

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int a[N],b[N];
struct asd{
	int l,r,sl,sr;
	int id;
}p[N];
int t1,t2;
int ln[N],rn[N];
int lm[N],rm[N];
int sum[N],c[N],sum1[N],c1[N];
int sum2[N],c2[N]; 
int posn[N],posm[N];
int cnt[N];
int data[N];//最终答案 
bool amp(asd a,asd b){
	if(posn[a.l]==posn[b.l]){
		return a.r<b.r;
	}
	return posn[a.l]<posn[b.l];
}
void add(int x){
	c[a[x]]+=b[x];
	c2[a[x]]=c[a[x]];
	sum[posm[a[x]]]=max(sum[posm[a[x]]],c[a[x]]);
	sum2[posm[a[x]]]=sum[posm[a[x]]];
}
void add1(int x){
	c[a[x]]+=b[x];
	sum[posm[a[x]]]=max(sum[posm[a[x]]],c[a[x]]);
}
void dele1(int x){
	c[a[x]]=c2[a[x]];
	sum[posm[a[x]]]=sum2[posm[a[x]]];
} 
int query(int x,int y){
	int l=posm[x],r=posm[y];
	int ma=0;
	if(l==r){
		for(int i=x;i<=y;i++) ma=max(ma,c[i]);
	}
	else{
		for(int i=l+1;i<=r-1;i++) ma=max(ma,sum[i]);
		for(int i=x;i<=rm[l];i++) ma=max(ma,c[i]);
		for(int i=lm[r];i<=y;i++) ma=max(ma,c[i]);
	}
	return ma;
}
signed main(){
	int n,m,Q;
	scanf("%lld%lld%lld",&n,&m,&Q);
	t1=sqrt(n);t2=sqrt(m);	
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld",&b[i]);
	}
	for(int i=1;i<=Q;i++){
		scanf("%lld%lld%lld%lld",&p[i].l,&p[i].r,&p[i].sl,&p[i].sr);
		p[i].id=i;
	}
	for(int i=1;i<=t1;i++){
		ln[i]=(i-1)*t1+1;
		rn[i]=t1*i;
	}
	if(rn[t1]<n) t1++,ln[t1]=rn[t1-1]+1,rn[t1]=n;
	for(int i=1;i<=t1;i++){
		for(int j=ln[i];j<=rn[i];j++){
			posn[j]=i;
		} 
	}
	for(int i=1;i<=t2;i++){
		lm[i]=(i-1)*t2+1;
		rm[i]=i*t2;
	}
	if(rm[t2]<m) t2++,lm[t2]=rm[t2-1]+1,rm[t2]=m;
	for(int i=1;i<=t2;i++){
		for(int j=lm[i];j<=rm[i];j++){
			posm[j]=i;
		}
	}
	sort(p+1,p+Q+1,amp);
	int l=1,r=0,block=0;
	int ma=0;
	for(int i=1;i<=Q;i++){
		int s1=posn[p[i].l],s2=posn[p[i].r];
		if(s1==s2){
			int tmp=0;
			for(int j=p[i].l;j<=p[i].r;j++){
				if(a[j]>=p[i].sl && a[j]<=p[i].sr){
					cnt[a[j]]+=b[j];
					tmp=max(tmp,cnt[a[j]]);
				}
			}
			for(int j=p[i].l;j<=p[i].r;j++){
				cnt[a[j]]=0;
			}
			data[p[i].id]=tmp;
			continue;
		}
		if(block^posn[p[i].l]){
			memset(sum,0,sizeof(sum));
			memset(c,0,sizeof(c));
			memset(sum2,0,sizeof(sum2));
			memset(c2,0,sizeof(c2));
			l=rn[posn[p[i].l]]+1,r=rn[posn[p[i].l]];
			ma=0,block=posn[p[i].l];
		}
		while(r<p[i].r) r++,add(r);
		int ll=l;
		while(ll>p[i].l) ll--,add1(ll);
		int tmp=query(p[i].sl,p[i].sr); 
		while(ll<rn[posn[p[i].l]]+1) dele1(ll),ll++;
		data[p[i].id]=tmp;
	}
	for(int i=1;i<=Q;i++){
		printf("%lld\n",data[i]);
	}
	
}
/*
5 3 3
1 3 2 3 2
1 2 1 1 2
1 4 1 3
1 4 1 2
2 3 1 1


*/ 

晚会#

思考三个人之间友好距离为 a,b,c 则一定有 a=bc 将边权从大到小排序,利用一个并查集维护每个连通块的大小与最小值,如果插入的边两点属于一个联通块,且边权不等于最小值,则不合法,不属于一个联通块,统计边权对答案的贡献,就是两个连通块的大小乘边权,加完后还不连通的点就值就是1。

csp模拟19#

最烂,没有之一!!!

十年之约#

输出打错了,挂60;

可爱三小只#

空间开小了,挂50;

蛋糕塌了#

首先可以想暴力, O(qn2)

修改操作考虑利用数据结构维护,对于区间 (l,r) 再考虑分治,中点 mid ,对于左端点在 (l,mid) 右端点在 (mid+1,r) 计算贡献为

pipj2ji+1=pi2midi+1pj2jmid

我们维护一个 h(l,m)=i=lmpi2mi+1 用来维护左侧的值,相对于 m 点来说, g(l,m)=i=lmpi2il+1 用来维护右侧,相对于 l 点来说。 所以就有如下方程:

g(l,r)=g(l,m)+g(m+1,r)2ml+1

h(l,r)=h(l,m)2rm+h(m+1,r)

区间答案就是 : ansl,r=ansl,m+ansm+1,r+h(l,m)g(m+1,r)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=6*1e5+10;
int base[N];
int ny;
int mgml(int x,int p){
	int ans=1;
	while(p){
		if(p&1) ans=(ans*x)%mod;
		x=(x*x)%mod;
		p>>=1;
	}
	return ans;
}
struct asd{
	int size,g,h,l,r;
	int f;
}tr[N*4];
struct qwe{
	int x,y;
}as[N*4];
int a[N*4],b[N*4],rk[N*4];
int tk[N];
int tot=0;
struct zxc{
	int data,fk,kl;
}st[N];
bool flat[N];
void pushup(int p){
	tr[p].size=tr[p*2].size+tr[p*2+1].size;
	tr[p].f=((tr[p*2].f+tr[p*2+1].f)%mod+tr[p*2].h*tr[p*2+1].g%mod)%mod;
	tr[p].g=(tr[p*2].g+tr[p*2+1].g*base[tr[p*2].size]%mod)%mod;
	tr[p].h=(tr[p*2].h*base[tr[p*2+1].size]%mod+tr[p*2+1].h)%mod;
}
void build(int p,int l,int r){
	tr[p].l=l,tr[p].r=r;
	if(l==r){
		if(flat[l]){
			tr[p].size=1;
			tr[p].f=0;
			tr[p].g=tr[p].h=rk[l]*ny%mod;
		}
		return;
	}
	int mid=(l+r)/2;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
	pushup(p);
}
void change(int p,int wh,int v){
	if(tr[p].l==tr[p].r){
		if(v==0){
			tr[p].size=0;
			tr[p].f=tr[p].g=tr[p].h=0;
		}
		else{
			tr[p].size=1;
			tr[p].f=0;
			tr[p].g=tr[p].h=rk[wh]*ny%mod;
		}
		return;
	}
	int mid=(tr[p].l+tr[p].r)/2;
	if(wh<=mid) change(p*2,wh,v);
	else change(p*2+1,wh,v);
	pushup(p);
}
bool amp(zxc a,zxc b){
	return a.data<b.data;
}
int p[N*4];
int n;
void init(){
	base[0]=1;
	for(int i=1;i<=n;i++){
		base[i]=base[i-1]*2%mod;
	}
	for(int i=1;i<=n;i++){
		base[i]=mgml(base[i],mod-2);
	}
	ny=mgml(2,mod-2);
}
inline int read(){
	int x(0);bool f(0);char ch=getchar();
	for(;ch<'0'||ch>'9';ch=getchar()) f^=ch=='-';
	for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
	return f?x=-x:x;
}
inline void write(int x){
	x<0?x=-x,putchar('-'):0;static short Sta[50],top(0);
	do{Sta[++top]=x%10;x/=10;}while(x);
	while(top) putchar(Sta[top--]|48);
	putchar('\n');
}
signed main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		st[++tot].data=a[i];
		st[tot].kl=i;
	}
	init();
	int q;
	q=read();
	for(int i=1;i<=q;i++){
		as[i].x=read(),as[i].y=read();
		st[++tot].data=as[i].y;
		st[tot].fk=i;
	}
	sort(st+1,st+tot+1,amp);
	for(int i=1;i<=tot;i++){
		rk[i]=st[i].data;
		if(st[i].kl){
			p[st[i].kl]=i;
			flat[i]=1;
		}
		if(st[i].fk){
			tk[st[i].fk]=i;
		}
	}
	build(1,1,tot);
	write(tr[1].f);
	for(int i=1;i<=q;i++){
		change(1,p[as[i].x],0);
		change(1,tk[i],1);
		p[as[i].x]=tk[i];
		write(tr[1].f);
	}
}
/*
4
3 4 5 5
4
1 5
2 5
3 5
4 5

*/

西安行#

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=1e7+10;
int n,m;
struct asd{
	int s[5][5];
	int l,r;
}a,b,f,c;
int d[N];
asd mul(asd s1,asd s2){
	asd z;
	memset(z.s,0,sizeof(z.s));
	for(int i=1;i<=s1.r;i++){
		for(int j=1;j<=s2.l;j++){
			for(int k=1;k<=s1.l;k++){
				z.s[j][i]+=s1.s[k][i]*s2.s[j][k]%mod;
				z.s[j][i]%=mod;
			}
		}
	}
	z.l=s2.l;
	z.r=s1.r;
	return z;
}
asd mgml(asd x,int p){
	asd z;
	memset(z.s,0,sizeof(z.s));
	for(int i=1;i<=3;i++) z.s[i][i]=1;
	z.l=z.r=3;
	while(p){
		if(p&1) z=mul(z,x);
		x=mul(x,x);
		p>>=1;
	}
	return z;
}
signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%lld",&d[i]);
	}
	a.l=a.r=b.l=b.r=c.l=c.r=3;
	a.s[1][1]=1,a.s[1][2]=0,a.s[1][3]=1;
	a.s[2][1]=2,a.s[2][2]=1,a.s[2][3]=2;
	a.s[3][1]=1,a.s[3][2]=1,a.s[3][3]=2;
	
	b.s[1][1]=1,b.s[1][2]=0,b.s[1][3]=0;
	b.s[2][1]=2,b.s[2][2]=1,b.s[2][3]=0;
	b.s[3][1]=1,b.s[3][2]=1,b.s[3][3]=1;
	
	f.l=3,f.r=1;
	f.s[1][1]=1;
	f.s[2][1]=2;
	f.s[3][1]=1;
	d[0]=0;
	for(int i=1;i<=m;i++){
		if(d[i]-d[i-1]>1)f=mul(f,mgml(a,d[i]-d[i-1]-1));
		f=mul(f,b);
	}
	if(d[m]<n-1){
		if(n-1-d[m]>0){
			f=mul(f,mgml(a,n-1-d[m]));
		}
	}
	int ans=0;
	cout<<f.s[3][1];
}

csp模拟20#

赞颂太阳#

看到与多个字符串长度有关,就可以想到AC自动机,然后按套路列出方程,
dp[i][j] 表示匹配到第 i 个以 j 结尾的最短长度,con[i][j] 表示 i 的后缀和 j 的前缀最大重合部分。con 可以用AC自动机预处理出,再看数据范围,用矩阵快速幂,这里有区别,并不是乘而是相加取 min.

dp[i][j]=d[i][k]+len[k]con[i][j]

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int a[N];
int b[N*2];
struct zxc{
	int op,b,c,d;
}g[N];
int cnt=0;
int c[N*4];
int lowbit(int x){
	return x&(-x);
}
void add(int wh,int v){
	if(wh==0) return;
	for(;wh<=cnt;wh+=lowbit(wh)) c[wh]+=v;
}
int ask(int wh){
	int ans=0;
	for(;wh;wh-=lowbit(wh)) ans+=c[wh];
	return ans;
}
struct asd{
	int l,r;
}kk[N];
int main(){
	freopen("darkteam.in","r",stdin);
	freopen("darkteam.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	b[++cnt]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[++cnt]=a[i];
	}
	for(int i=1;i<=m;i++){
		int op,x,y;
		scanf("%d",&g[i].op);
		if(g[i].op==1){
			scanf("%d",&g[i].b);
			b[++cnt]=g[i].b;
		}
		else{
			scanf("%d%d",&g[i].c,&g[i].d);
			b[++cnt]=g[i].d;
		}
	}
	sort(b+1,b+cnt+1);
	int tot=unique(b+1,b+cnt+1)-(b+1);
	for(int i=0;i<=n;i++){
		int s1=lower_bound(b+1,b+tot+1,a[i])-(b);
		int s2=lower_bound(b+1,b+tot+1,a[i+1])-(b);
		int l=min(s1,s2)+1,r=max(s1,s2);
		add(l,1),add(r+1,-1);
	}
	for(int i=1;i<=m;i++){
		if(g[i].op==1){
			int s1=lower_bound(b+1,b+tot+1,g[i].b)-(b);
			int ans;
			ans=ask(s1);
			cout<<ans/2<<endl;
		}
		else{
			int s1=lower_bound(b+1,b+tot+1,a[g[i].c-1])-(b);
			int s2=lower_bound(b+1,b+tot+1,a[g[i].c])-(b);
			int s3=lower_bound(b+1,b+tot+1,a[g[i].c+1])-(b);
			int s4=lower_bound(b+1,b+tot+1,g[i].d)-(b);
			a[g[i].c]=g[i].d;
			int l=min(s1,s2)+1,r=max(s1,s2);
			add(l,-1),add(r+1,1);
			l=min(s2,s3)+1,r=max(s2,s3);
			add(l,-1),add(r+1,1);
			l=min(s4,s1)+1,r=max(s4,s1);
			add(l,1),add(r+1,-1);
			l=min(s4,s3)+1,r=max(s3,s4);
			add(l,1),add(r+1,-1);
		}
	}
}

幽邃主教群#

对于相邻的两个数 x,y 以及查询 B ,则 min(x,y)+1max(x,y) 则会造成贡献为1,最后总贡献除以二就是最后答案,因为要修改,所以可以用树状数组差分维护(相邻两个点在一段区间有贡献),更改时删掉原来的在加上现在的。还要使用离散化,注意处理 0n+1 对两头贡献。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=200005;
int a[N];
int b[N*2];
struct zxc{
	int op,b,c,d;
}g[N];
int cnt=0;
int c[N*4];
int lowbit(int x){
	return x&(-x);
}
void add(int wh,int v){
	if(wh==0) return;
	for(;wh<=cnt;wh+=lowbit(wh)) c[wh]+=v;
}
int ask(int wh){
	int ans=0;
	for(;wh;wh-=lowbit(wh)) ans+=c[wh];
	return ans;
}
struct asd{
	int l,r;
}kk[N];
int main(){
	freopen("darkteam.in","r",stdin);
	freopen("darkteam.out","w",stdout);
	int n,m;
	scanf("%d%d",&n,&m);
	b[++cnt]=0;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[++cnt]=a[i];
	}
	for(int i=1;i<=m;i++){
		int op,x,y;
		scanf("%d",&g[i].op);
		if(g[i].op==1){
			scanf("%d",&g[i].b);
			b[++cnt]=g[i].b;
		}
		else{
			scanf("%d%d",&g[i].c,&g[i].d);
			b[++cnt]=g[i].d;
		}
	}
	sort(b+1,b+cnt+1);
	int tot=unique(b+1,b+cnt+1)-(b+1);
	for(int i=0;i<=n;i++){
		int s1=lower_bound(b+1,b+tot+1,a[i])-(b);
		int s2=lower_bound(b+1,b+tot+1,a[i+1])-(b);
		int l=min(s1,s2)+1,r=max(s1,s2);
		add(l,1),add(r+1,-1);
	}
	for(int i=1;i<=m;i++){
		if(g[i].op==1){
			int s1=lower_bound(b+1,b+tot+1,g[i].b)-(b);
			int ans;
			ans=ask(s1);
			cout<<ans/2<<endl;
		}
		else{
			int s1=lower_bound(b+1,b+tot+1,a[g[i].c-1])-(b);
			int s2=lower_bound(b+1,b+tot+1,a[g[i].c])-(b);
			int s3=lower_bound(b+1,b+tot+1,a[g[i].c+1])-(b);
			int s4=lower_bound(b+1,b+tot+1,g[i].d)-(b);
			a[g[i].c]=g[i].d;
			int l=min(s1,s2)+1,r=max(s1,s2);
			add(l,-1),add(r+1,1);
			l=min(s2,s3)+1,r=max(s2,s3);
			add(l,-1),add(r+1,1);
			l=min(s4,s1)+1,r=max(s4,s1);
			add(l,1),add(r+1,-1);
			l=min(s4,s3)+1,r=max(s3,s4);
			add(l,1),add(r+1,-1);
		}
	}
}

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17619315.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(40)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu