【做题记录】2025暑假-数据结构专题

A. Closest Equals

首先给每个位置求出它到上一次出现的距离,将这记为一条线段。我们发现,有效的线段,其随着左端点的增长,右端点必然是增长的。因为如果有一条大线段包含了小线段,大线段必然是没用的。于是我们便可以二分出查询的线段区间,ST 表求出最小值即可。

Code
#include<bits/stdc++.h>
#define il inline
#define lwrb lower_bound
#define uprb upper_bound
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,m,cnt,ll[maxn],rr[maxn],Log[maxn],st[maxn][22];
map<int,int> pos;
il int query(int l,int r){
	if(l>r){
		return -1;
	}
	int p=Log[r-l+1];
	return min(st[l][p],st[r-(1<<p)+1][p]);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1,j,x;i<=n;i++){
		cin>>x;
		j=pos[x];
		if(j>ll[cnt]){
			ll[++cnt]=j,rr[cnt]=i;
		}
		pos[x]=i;
	}
	for(int i=2;i<=cnt;i++){
		Log[i]=Log[i>>1]+1;
	}
	for(int i=1;i<=cnt;i++){
		st[i][0]=rr[i]-ll[i];
	}
	for(int j=1;j<=Log[cnt];j++){
		for(int i=1;i+(1<<j)-1<=cnt;i++){
			st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
		}
	}
	while(m--){
		int l,r;
		cin>>l>>r;
		cout<<query(lwrb(ll+1,ll+cnt+1,l)-ll,uprb(rr+1,rr+cnt+1,r)-rr-1)<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

B. Fibonacci-ish II

考虑莫队,用权值线段树维护矩阵。当新加入一个数时,要给它后面的数全都乘上 \(\begin{bmatrix}0&1\\1&1\end{bmatrix}\);删除时,要给后面都乘上逆矩阵。时间复杂度 \(O(n\sqrt{n}\log n)\),比较卡常,注意传参的时间消耗。卡正解常放暴力过的出题人是屑

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace IO{
	const int bufsz=1<<20;
	char ibuf[bufsz],*p1=ibuf,*p2=ibuf;
	#define getchar() (p1==p2&&(p2=(p1=ibuf)+fread(ibuf,1,bufsz,stdin),p1==p2)?EOF:*p1++)
	il int read(){
		char ch=getchar();
		while(ch<'0'||ch>'9'){
			ch=getchar();
		}
		int x=0;
		while(ch>='0'&&ch<='9'){
			x=(x<<1)+(x<<3)+(ch^48);
			ch=getchar();
		}
		return x;
	}
	#undef getchar
}
using IO::read;
const int maxn=3e4+5,bln=173;
int n,m,mod,a[maxn],lsh[maxn],tot,sz[maxn<<2],tag[maxn<<2],cnt[maxn],bel[maxn],ans[maxn];
struct node{
	int l,r,id;
	il bool operator<(const node &x)const{
		return (bel[l]^bel[x.l])?l<x.l:bel[l]&1?r>x.r:r<x.r;
	}
}wt[maxn];
struct juz{
	int a00,a01,a10,a11;
	juz(int a00=0,int a01=0,int a10=0,int a11=0):a00(a00),a01(a01),a10(a10),a11(a11){}
	il juz operator+(const juz &x)const{
		#define pls(x,y) x+y<mod?x+y:x+y-mod
		return juz(pls(a00,x.a00),pls(a01,x.a01),pls(a10,x.a10),pls(a11,x.a11));
		#undef pls
	}
	il juz operator*(const juz &x)const{
		int t1=a00*x.a00+a01*x.a10,t2=a00*x.a01+a01*x.a11,t3=a10*x.a00+a11*x.a10,t4=a10*x.a01+a11*x.a11;
		#define qum(x) x<mod?x:x%mod
		return juz(qum(t1),qum(t2),qum(t3),qum(t4));
		#undef qum
	}
	il bool operator!=(const juz &x)const{
		return (a00^x.a00)||(a01^x.a01)||(a10^x.a10)||(a11^x.a11);
	}
}A,B,I,SA[maxn],SB[maxn],f[maxn],sum[maxn<<2];
il void pushup(int id){
	sum[id]=sum[lid]+sum[rid];
	sz[id]=sz[lid]+sz[rid];
}
il void pushtag(int id,int v){
	tag[id]+=v;
	juz t=v>0?SA[v]:SB[-v];
	sum[id]=sum[id]*t;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void mul(int id,int L,int R,int l,int r,int v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		mul(lid,L,mid,l,r,v);
	}
	if(r>mid){
		mul(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il void upd(int id,int l,int r,int p,int v,int rk){
	if(l==r){
		sum[id]=f[rk+1]*juz(v,0,0,v),sz[id]^=1;
		return ;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(p<=mid){
		upd(lid,l,mid,p,v,rk);
	}
	else{
		upd(rid,mid+1,r,p,v,sz[lid]+rk);
	}
	pushup(id);
}
il void add(int p){
	if(cnt[p]++==0){
		upd(1,1,tot,p,lsh[p],0);
		if(p<tot){
			mul(1,1,tot,p+1,tot,1);
		}
	}
}
il void del(int p){
	if(--cnt[p]==0){
		upd(1,1,tot,p,0,0);
		if(p<tot){
			mul(1,1,tot,p+1,tot,-1);
		}
	}
}
int main(){
//	freopen("B.in","r",stdin);
//	freopen("my.out","w",stdout);
	n=read(),mod=read();
	A=juz(0,1,1,1),B=juz(mod-1,1,1,0),I=juz(1,0,0,1);
	SA[0]=SB[0]=I;
	for(int i=1;i<=n;i++){
		a[i]=read();
		lsh[++tot]=a[i];
		bel[i]=i/bln;
		SA[i]=SA[i-1]*A;
		SB[i]=SB[i-1]*B;
	}
	sort(lsh+1,lsh+tot+1);
	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
	for(int i=1;i<=n;i++){
		a[i]=lwrb(lsh+1,lsh+tot+1,a[i])-lsh;
	}
	for(int i=1;i<=tot;i++){
		lsh[i]%=mod;
	}
	f[1]=juz(0,1);
	for(int i=2;i<=n;i++){
		f[i]=f[i-1]*A;
	}
	m=read();
	for(int i=1;i<=m;i++){
		wt[i].l=read(),wt[i].r=read(),wt[i].id=i;
	}
	sort(wt+1,wt+m+1);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		while(l>wt[i].l){
			add(a[--l]);
		}
		while(r<wt[i].r){
			add(a[++r]);
		}
		while(l<wt[i].l){
			del(a[l++]);
		}
		while(r>wt[i].r){
			del(a[r--]);
		}
		ans[wt[i].id]=sum[1].a01;
	}
	for(int i=1;i<=m;i++){
		printf("%d\n",ans[i]);
	}
//	cerr<<clock()<<"\n";
	return 0;
}
/*
5 1000
1 5 2 3 2 
5
4 5
2 4
1 4
1 3
4 4
5
15
24
13
3
*/

C. Sasha and Array

沿用上一道题的思路,仍然用线段树维护矩阵即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5,mod=1e9+7;
int n,m,a[maxn];
struct juz{
	int mat[2][2];
	juz(){
		mat[0][0]=mat[0][1]=mat[1][0]=mat[1][1]=0;
	}
	il int*operator[](int x){
		return mat[x];
	}
	il juz operator+(juz x)const{
		juz res;
		for(int i=0;i<=1;i++){
			for(int j=0;j<=1;j++){
				res[i][j]=(mat[i][j]+x[i][j])%mod;
			}
		}
		return res;
	}
	il juz operator*(juz x)const{
		juz res;
		for(int i=0;i<=1;i++){
			for(int j=0;j<=1;j++){
				for(int k=0;k<=1;k++){
					(res[i][j]+=mat[i][k]*1ll*x[k][j]%mod)%=mod;
				}
			}
		}
		return res;
	}
	il bool operator!=(juz x)const{
		for(int i=0;i<=1;i++){
			for(int j=0;j<=1;j++){
				if(mat[i][j]!=x[i][j]){
					return 1;
				}
			}
		}
		return 0;
	}
}A,I,sum[maxn<<2],tag[maxn<<2];
il juz qpow(juz x,int y){
	juz res;
	res[0][0]=res[1][1]=1;
	for(;y;y>>=1,x=x*x){
		if(y&1){
			res=res*x;
		}
	}
	return res;
}
il void pushup(int id){
	sum[id]=sum[lid]+sum[rid];
}
il void pushtag(int id,juz v){
	tag[id]=tag[id]*v;
	sum[id]=sum[id]*v;
}
il void pushdown(int id){
	if(tag[id]!=I){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=I;
	}
}
il void build(int id,int l,int r){
	tag[id]=I;
	if(l==r){
		sum[id]=qpow(A,a[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void mul(int id,int L,int R,int l,int r,juz v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		mul(lid,L,mid,l,r,v);
	}
	if(r>mid){
		mul(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il juz query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return sum[id];
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(r<=mid){
		return query(lid,L,mid,l,r);
	}
	if(l>mid){
		return query(rid,mid+1,R,l,r);
	}
	return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	A[0][1]=A[1][0]=A[1][1]=I[0][0]=I[1][1]=1;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	while(m--){
		int opt,l,r,x;
		cin>>opt>>l>>r;
		if(opt==1){
			cin>>x;
			mul(1,1,n,l,r,qpow(A,x));
		}
		else{
			cout<<query(1,1,n,l,r)[0][1]<<"\n";
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

D. New Year Tree

发现 \(c\) 很小,于是考虑开 long long 状压,dfn+ 线段树即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
#define popcntll __builtin_popcountll
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=4e5+5;
int n,m,cnt,a[maxn],L[maxn],R[maxn],stk[maxn];
ll sum[maxn<<2],tag[maxn<<2];
vector<int> e[maxn];
il void dfs(int u,int fa){
	stk[++cnt]=u,L[u]=cnt;
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs(v,u);
	}
	R[u]=cnt;
}
il void pushup(int id){
	sum[id]=sum[lid]|sum[rid];
}
il void pushtag(int id,ll v){
	sum[id]=tag[id]=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void build(int id,int l,int r){
	if(l==r){
		sum[id]=1ll<<a[stk[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void upd(int id,int L,int R,int l,int r,ll v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		upd(lid,L,mid,l,r,v);
	}
	if(r>mid){
		upd(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il ll query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return sum[id];
	}
	pushdown(id);
	int mid=(L+R)>>1;
	ll res=0;
	if(l<=mid){
		res|=query(lid,L,mid,l,r);
	}
	if(r>mid){
		res|=query(rid,mid+1,R,l,r);
	}
	return res;
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	dfs(1,0),build(1,1,n);
	while(m--){
		int opt,u,x;
		cin>>opt>>u;
		if(opt==1){
			cin>>x;
			upd(1,1,n,L[u],R[u],1ll<<x);
		}
		else{
			cout<<popcntll(query(1,1,n,L[u],R[u]))<<"\n";
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

E. Lucky Array

首先将所有 lucky numbers 都打出来,发现 \(1e4\) 以内只有 \(30\) 个。考虑线段树,在每个下标存储该位置与第一个 \(\ge\) 它的幸运数之差,维护区间最小值及个数,于是区间修改就成区间减了。当有的数被减成了负数,那就把这个位置暴力更新出来。每个位置最多更新 \(30\) 次,于是时间复杂度 \(O(30n\log n)\)

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=1e5+5,inf=1e9;
int n,m,cnt,a[maxn],b[maxn],tag[maxn<<2]; 
struct seg{
	int val,cnt;
	seg(int val=0,int cnt=0):val(val),cnt(cnt){}
	il seg operator+(const seg &x)const{
		if(val==x.val){
			return seg(val,cnt+x.cnt);
		}
		return val<x.val?*this:x;
	}
}tr[maxn<<2];
il void pushup(int id){
	tr[id]=tr[lid]+tr[rid];
}
il void pushtag(int id,int v){
	tag[id]+=v,tr[id].val+=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void reset(int id,int &x){
	int t=*lwrb(b+1,b+cnt+1,x);
	tr[id]=seg(t-x,1),x=t;
}
il void build(int id,int l,int r){
	if(l==r){
		reset(id,a[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		add(lid,L,mid,l,r,v);
	}
	if(r>mid){
		add(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il void upd(int id,int l,int r){
	if(l==r){
		a[l]-=tr[id].val;
		reset(id,a[l]);
		return ;
	}
	pushdown(id);
	int mid=(l+r)>>1;
	if(tr[lid].val<0){
		upd(lid,l,mid);
	}
	else{
		upd(rid,mid+1,r);
	}
	pushup(id);
}
il seg query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return tr[id];
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(r<=mid){
		return query(lid,L,mid,l,r);
	}
	if(l>mid){
		return query(rid,mid+1,R,l,r);
	}
	return query(lid,L,mid,l,r)+query(rid,mid+1,R,l,r);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	for(int i=1,x;i<=1e4;i++){
		x=i;
		while(x){
			if(x%10!=4&&x%10!=7){
				goto togo;
			}
			x/=10;
		}
		b[++cnt]=i;
		togo:;
	}
	b[++cnt]=inf;
//	cout<<cnt<<"\n";
//	for(int i=1;i<=cnt;i++){
//		cout<<b[i]<<"\n";
//	}
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	while(m--){
		string opt;
		int l,r,x;
		cin>>opt>>l>>r;
		if(opt[0]=='a'){
			cin>>x;
			add(1,1,n,l,r,-x);
			while(tr[1].val<0){
				upd(1,1,n);
			}
		}
		else{
			seg ans=query(1,1,n,l,r);
			cout<<(ans.val?0:ans.cnt)<<"\n";
		}
	}
	return 0;
}
}
int main(){return asbt::main();}

F. Escape Through Leaf

\(dp_u\) 表示 \(u\) 的答案,于是有方程:\(dp_u=\min_{v\in subtree_u}\{dp_v+b_v\times a_u\}\),显然的李超线段树。由于要用子树中的点更新答案,所以需要线段树合并。根据势能分析时间复杂度 \(O(n\log n)\)。通过精细实现可以做到空间线性。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pb push_back
using namespace std;
namespace asbt{
const int maxn=1e5+5;
const ll inf=1e18;
int n,cnt,rt[maxn],ls[maxn],rs[maxn],tr[maxn];
ll a[maxn],b[maxn],dp[maxn];
vector<int> e[maxn];
struct{
	ll k,b;
	il ll calc(int x){
		return k*x+b;
	}
}xd[maxn];
il void insert(int &p,int l,int r,int q){
	if(!p){
		p=q;
		ls[p]=rs[p]=0;
		return ;
	}
	ll lp=xd[tr[p]].calc(l),rp=xd[tr[p]].calc(r);
	ll lq=xd[tr[q]].calc(l),rq=xd[tr[q]].calc(r);
	if(lp>=lq&&rp>=rq){
		swap(tr[p],tr[q]);
		swap(lp,lq),swap(rp,rq);
	}
	if(lp<=lq&&rp<=rq){
		return ;
	}
	int mid=(l+r)>>1;
	if(xd[tr[p]].calc(mid)>xd[tr[q]].calc(mid)){
		swap(tr[p],tr[q]);
		swap(lp,lq),swap(rp,rq);
	}
	if(lq<lp){
		insert(ls[p],l,mid,q);
	}
	else{
		insert(rs[p],mid+1,r,q);
	}
}
il int merge(int p,int q,int l,int r){
	if(!p||!q){
		return p+q;
	}
	if(l==r){
		return xd[tr[p]].calc(l)<xd[tr[q]].calc(l)?p:q;
	}
	int mid=(l+r)>>1;
	ls[p]=merge(ls[p],ls[q],l,mid);
	rs[p]=merge(rs[p],rs[q],mid+1,r);
	insert(p,l,r,q);
	return p;
}
il ll query(int id,int l,int r,int p){
	if(!id){
		return inf;
	}
	ll res=xd[tr[id]].calc(p);
	if(l==r){
		return res;
	}
	int mid=(l+r)>>1;
	return min(res,p<=mid?query(ls[id],l,mid,p):query(rs[id],mid+1,r,p));
}
il void dfs(int u,int fa){
	for(int v:e[u]){
		if(v==fa){
			continue;
		}
		dfs(v,u);
		rt[u]=merge(rt[u],rt[v],-1e5,1e5);
	}
	if(rt[u]){
//		cout<<u<<"\n";
		dp[u]=query(rt[u],-1e5,1e5,a[u]);
	}
	xd[u].k=b[u],xd[u].b=dp[u];
	tr[++cnt]=u;
	insert(rt[u],-1e5,1e5,cnt);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		e[u].pb(v),e[v].pb(u);
	}
	dfs(1,0);
	for(int i=1;i<=n;i++){
		cout<<dp[i]<<" ";
	}
	return 0;
}
}
int main(){return asbt::main();}

G. T-Shirts

考虑排序后遍历每件 T 恤衫,对 \(\ge c_i\) 的人进行修改。发现并不好维护。考虑 FHQ-Treap,将平衡树分裂为三个部分:\([0,c)\)\([c,2c)\)\([2c,+\infty)\)。显然第一部分不用动,第二部分修改后会 \(<c\),第三部分修改后仍 \(\ge c\)。于是对第二部分直接暴力修改后插入第一部分,第三部分打上懒标记再与第一部分合并即可。考虑时间复杂度,瓶颈显然在第二部分上。发现每次顶多变为原来的一半,因此每个数最多暴力修改 \(\log\) 次。于是时间复杂度 \(O(n\log^2)\)。CF 再次爆炸,因此再次无法贴代码(

upd:CF 又好了

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
using namespace std;
namespace asbt{
const int maxn=2e5+5;
int n,m,rt,tot,ls[maxn],rs[maxn];
int val[maxn],key[maxn],tag[maxn];
int ans[maxn],tans[maxn];
struct node{
	int a,b;
	il bool operator<(const node &x)const{
		return b>x.b||b==x.b&&a<x.a;
	}
}p[maxn];
mt19937 rd(time(0));
il int New(int x){
	int p=++tot;
	ls[p]=rs[p]=tag[p]=0;
	val[p]=x,key[p]=rd();
	return p;
}
il void pushdown(int p){
	if(tag[p]){
		tag[ls[p]]+=tag[p];
		tag[rs[p]]+=tag[p];
		val[ls[p]]+=tag[p];
		val[rs[p]]+=tag[p];
		tag[p]=0;
	}
	if(tans[p]){
		tans[ls[p]]+=tans[p];
		tans[rs[p]]+=tans[p];
		ans[ls[p]]+=tans[p];
		ans[rs[p]]+=tans[p];
		tans[p]=0;
	}
}
il int merge(int p,int q){
	if(!p||!q){
		return p+q;
	}
	if(key[p]<key[q]){
		pushdown(p);
		rs[p]=merge(rs[p],q);
		return p;
	}
	else{
		pushdown(q);
		ls[q]=merge(p,ls[q]);
		return q;
	}
}
il void split(int id,int &p,int &q,int x){
	if(!id){
		p=q=0;
		return ;
	}
	pushdown(id);
	if(val[id]<x){
		p=id;
		split(rs[id],rs[p],q,x);
	}
	else{
		q=id;
		split(ls[id],p,ls[q],x);
	}
}
il void insert(int &id,int x){
	int p,q;
	split(id,p,q,val[x]);
	id=merge(merge(p,x),q);
}
il void dfs(int id,int v,int &x){
	if(!id){
		return ;
	}
	pushdown(id);
	dfs(ls[id],v,x),dfs(rs[id],v,x);
	ls[id]=0,rs[id]=0,val[id]-=v,ans[id]++;
	insert(x,id);
}
il void push(int id){
	if(!id){
		return ;
	}
	pushdown(id);
	push(ls[id]),push(rs[id]);
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>p[i].a>>p[i].b;
	}
	cin>>m;
	for(int i=1,x;i<=m;i++){
		cin>>x;
		insert(rt,New(x));
	}
//	puts("666");
	sort(p+1,p+n+1);
	for(int i=1;i<=n;i++){
//		cout<<p[i].a<<" "<<p[i].b<<"\n";
//		cout<<i<<"\n";
		int x,y,z;
//		puts("666");
		split(rt,x,y,p[i].a);
//		puts("777");
		split(y,y,z,p[i].a<<1);
//		puts("888");
		tag[z]-=p[i].a,val[z]-=p[i].a;
		tans[z]++,ans[z]++;
		dfs(y,p[i].a,x);
		rt=merge(x,z);
//	for(int j=1;j<=m;j++){
//		cout<<ans[j]<<" ";
//	}
//	cout<<"\n";
	}
//	puts("666");
	push(rt);
	for(int i=1;i<=m;i++){
		cout<<ans[i]<<" ";
	}
	return 0;
}
}
int main(){return asbt::main();}

H. New task

\((a_1,a_2,a_3,a_4,a_5)\) 分为 \(3\) 个部分,即 \((a_1,a_2)\)\(a_3\)\((a_4,a_5)\),分别记为 \(a\)\(b\)\(c\)。因为要求 \(a_2=a_3=a_4\),考虑在 \(a_2\) 维护 \(a\),在 \(a_4\) 维护 \(c\),对每个 \(a\) 值开线段树,维护一段区间的 \(a\)\(b\)\(c\)\(ab\)\(bc\)\(abc\) 的数量。树状数组预处理出 \(a\)\(c\) 即可。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define lwrb lower_bound
using namespace std;
namespace asbt{
const int maxn=1e5+5,mod=1e9+7;
il int pls(int x,int y){
	return x+y<mod?x+y:x+y-mod;
}
il int sub(int x,int y){
	return x<y?x-y+mod:x-y;
}
int n,m,a[maxn],lsh[maxn],tot,qan[maxn],hou[maxn],rt[maxn];
struct{
	int tr[maxn];
	il int lowbit(int x){
		return x&-x;
	}
	il void build(){
		memset(tr,0,sizeof(tr));
	}
	il void add(int p,int v){
		for(;p<=n;p+=lowbit(p)){
			tr[p]+=v;
		}
	}
	il int query(int p){
		int res=0;
		for(;p;p-=lowbit(p)){
			res+=tr[p];
		}
		return res;
	}
}F;
struct seg{
	int a,b,c,ab,bc,abc;
	seg(int a=0,int b=0,int c=0,int ab=0,int bc=0,int abc=0)
		:a(a),b(b),c(c),ab(ab),bc(bc),abc(abc){}
	il seg operator+(const seg &x)const{
		seg res;
		res.a=pls(a,x.a),res.b=pls(b,x.b),res.c=pls(c,x.c);
		res.ab=pls(pls(ab,x.ab),a*1ll*x.b%mod);
		res.bc=pls(pls(bc,x.bc),b*1ll*x.c%mod);
		res.abc=pls(pls(abc,x.abc),pls(ab*1ll*x.c%mod,a*1ll*x.bc%mod));
		return res;
	}
};
struct{
	int tot,ls[maxn*18],rs[maxn*18];
	seg tr[maxn*18];
	il void pushup(int id){
		tr[id]=tr[ls[id]]+tr[rs[id]];
	}
	il void upd(int &id,int l,int r,int p,seg v){
		if(!id){
			id=++tot;
		}
		if(l==r){
			tr[id]=v;
			return ;
		}
		int mid=(l+r)>>1;
		if(p<=mid){
			upd(ls[id],l,mid,p,v);
		}
		else{
			upd(rs[id],mid+1,r,p,v);
		}
		pushup(id);
	}
}S;
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		lsh[++tot]=a[i];
	}
	sort(lsh+1,lsh+tot+1);
	tot=unique(lsh+1,lsh+tot+1)-lsh-1;
	for(int i=1;i<=n;i++){
		a[i]=lwrb(lsh+1,lsh+tot+1,a[i])-lsh;
		qan[i]=F.query(a[i]);
		F.add(a[i],1);
	}
	F.build();
	for(int i=n;i;i--){
		hou[i]=F.query(a[i]);
		F.add(a[i],1);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		S.upd(rt[a[i]],1,n,i,seg(qan[i],1,hou[i]));
	}
	for(int i=1;i<=tot;i++){
		ans=pls(ans,S.tr[rt[i]].abc);
	}
//	cout<<ans<<"\n";
	cin>>m;
	while(m--){
		int opt,x;
		cin>>opt>>x;
		ans=sub(ans,S.tr[rt[a[x]]].abc);
		S.upd(rt[a[x]],1,n,x,opt==1?seg():seg(qan[x],1,hou[x]));
		ans=pls(ans,S.tr[rt[a[x]]].abc);
		cout<<ans<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}

I. Blog Post Rating

首先可以证明的是,当用户单调不降,答案一定是最优的。证明考虑临项交换法,设 \(x>y\),那么由下表可知 \((x,y)\) 肯定不比 \((y,x)\) 优:

image

我们还可以发现的是,随着 \(a_i\) 从小到大,答案一定是先单调递减,再单调不降。那么我们考虑维护一个按 \(a_i\) 大小排序的线段树,维护每个位置的 \(a_i\) 与经过当前位置后的答案之差。我们再用三个 set 维护贡献为 \(0\)\(1\)\(-1\) 的集合。考虑一个新加入的用户,对其贡献分类讨论:

  • \(0\),则直接将其加入集合即可,对后面的用户没有影响。
  • \(-1\),首先此时它及其之后的差值都会加一,这可能导致最后一个 \(-1\) 变为 \(0\)。如果最后一个 \(-1\) 不变,那么第一个 \(0\) 将变成 \(1\)
  • \(1\),它及之后的差值全都减一,那么在它之后可能有一个 \(1\) 变成了 \(0\)

用线段树维护区间最小值及其最小下标即可。时间复杂度线性对数。

Code
#include<bits/stdc++.h>
#define ll long long
#define il inline
#define pii pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define lid id<<1
#define rid id<<1|1
using namespace std;
namespace asbt{
const int maxn=5e5+5;
int n,a[maxn],p[maxn],fp[maxn],tag[maxn<<2];
pii mn[maxn<<2];
set<int> _0,_1,__1;
il void pushup(int id){
	mn[id]=min(mn[lid],mn[rid]);
}
il void pushtag(int id,int v){
	tag[id]+=v,mn[id].fir+=v;
}
il void pushdown(int id){
	if(tag[id]){
		pushtag(lid,tag[id]);
		pushtag(rid,tag[id]);
		tag[id]=0;
	}
}
il void build(int id,int l,int r){
	if(l==r){
		mn[id]=mp(a[p[l]],l);
		return ;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
il void add(int id,int L,int R,int l,int r,int v){
	if(L>=l&&R<=r){
		pushtag(id,v);
		return ;
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(l<=mid){
		add(lid,L,mid,l,r,v);
	}
	if(r>mid){
		add(rid,mid+1,R,l,r,v);
	}
	pushup(id);
}
il pii query(int id,int L,int R,int l,int r){
	if(L>=l&&R<=r){
		return mn[id];
	}
	pushdown(id);
	int mid=(L+R)>>1;
	if(r<=mid){
		return query(lid,L,mid,l,r);
	}
	if(l>mid){
		return query(rid,mid+1,R,l,r);
	}
	return min(query(lid,L,mid,l,r),query(rid,mid+1,R,l,r));
}
int main(){
	ios::sync_with_stdio(0),cin.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i],p[i]=i;
	}
	sort(p+1,p+n+1,[](int x,int y){return a[x]<a[y];});
	for(int i=1;i<=n;i++){
		fp[p[i]]=i;
	}
	build(1,1,n);
	for(int i=1;i<=n;i++){
		pii val=query(1,1,n,fp[i],fp[i]);
		if(val.fir==0){
			_0.insert(fp[i]);
		}
		else if(val.fir<0){
			__1.insert(fp[i]);
			add(1,1,n,fp[i],n,1);
			int t=*__1.rbegin();
			if(a[p[t]]==1-(int)__1.size()){
				__1.erase(t),_0.insert(t);
				add(1,1,n,t,n,-1);
			}
			else if(_0.size()){
				t=*_0.begin();
				_0.erase(t),_1.insert(t);
				add(1,1,n,t,n,-1);
			}
		}
		else{
			_1.insert(fp[i]);
			add(1,1,n,fp[i],n,-1);
			val=query(1,1,n,fp[i],n);
			if(val.fir<0){
				_1.erase(val.sec),_0.insert(val.sec);
				add(1,1,n,val.sec,n,1);
			}
		}
		cout<<(int)_1.size()-(int)__1.size()<<"\n";
	}
	return 0;
}
}
int main(){return asbt::main();}
posted @ 2025-07-23 21:03  zhangxy__hp  阅读(29)  评论(0)    收藏  举报