其他分享
首页 > 其他分享> > 【考试总结】2022-07-13

【考试总结】2022-07-13

作者:互联网

计蒜客题

从左到右枚举区间右端点并求出来有几个左端点满足条件

单调栈维护当前遍历到的 \(i\) 为右端点的区间的最大/最小值,每个栈中元素作为最值的区间在它加入栈之后不会延长,那么计算出来这段区间里面有哪些满足条件。

入栈的时候求出,弹栈的时候删掉即可

Code Display
const int N=1e6+10;
int n,x,a[N],ans;
int mnstk[N],mntop,mnl[N];
int mxstk[N],mxtop,mxl[N];
map<int,int> Mn,Mx;
vector<pair<int,int> > mnc[N],mxc[N];
signed main(){
	freopen("garlic.in","r",stdin); freopen("garlic.out","w",stdout);
	n=read(); x=read();
	int val=0;
	for(int i=1;i<=n;++i){
		a[i]=read();
		while(mntop&&a[i]<=a[mnstk[mntop]]){
			int pos=mnstk[mntop];
			Mn.erase(a[pos]);
			for(auto t:mnc[pos]){
				if(Mx[a[t.fir]]!=t.fir) continue;
				val-=t.sec;
			}
			--mntop;
		}
		while(mxtop&&a[i]>=a[mxstk[mxtop]]){
			int pos=mxstk[mxtop];
			Mx.erase(a[pos]);
			for(auto t:mxc[pos]){
				if(Mn[a[t.fir]]!=t.fir) continue;
				val-=t.sec;
			}
			--mxtop;
		}
		//insert stack
		mnstk[++mntop]=i; 
		Mn[a[i]]=i; 
		mnl[i]=1+mnstk[mntop-1];
		mxstk[++mxtop]=i; 
		Mx[a[i]]=i; 
		mxl[i]=1+mxstk[mxtop-1];
		if(Mn.count(x-a[i])){
			int pos=Mn[x-a[i]];
			int cros=max(0ll,1+min(i,pos)-max(mnl[pos],mxl[i]));
			if(cros){
				mnc[pos].emplace_back(i,cros);
				mxc[i].emplace_back(pos,cros);
				val+=cros;
			}
		}
		if(Mx.count(x-a[i])&&x!=a[i]*2){
			int pos=Mx[x-a[i]];
			int cros=max(0ll,1+min(i,pos)-max(mxl[pos],mnl[i]));
			if(cros){
				mxc[pos].emplace_back(i,cros);
				mnc[i].emplace_back(pos,cros);
				val+=cros;
			}
		}
		ans+=val;
	}
	print(ans);
	return 0;
}

奶牛题

从 \(1\sim m\) 枚举能操作最高位,此时如果有路径上的一个点操作了最高位那么以后就不能操作最高位了,变成了两个子问题

设 \(f_{h,i,j}\) 表示操作的最高位不超过第 \(h\) 位,不钦定第一步最后一步按在哪里的情况下从 \(i\) 到 \(j\) 的方案数。

如果没有操作最高位的元素就使用 \(f_{h-1,i,j}\) 转移,否则枚举最高位操作处 \(k\) 以及路径上的前驱后继 \(p,n\) 用 \(f_{h-1,i,p}\times f_{h-1,n,j}\) 转移

将所有的可行 \(p,n\) 求和再乘积复杂度 \(\Theta(n^3)\)

这个转移的用处在于询问的时候第一步最后一步被钦定了,那么将 \(n\) 个点变成 \(n+Q\) 个节点。此时 \(f_{h,i,j}\) 在 \(i,j\le n\) 时含义不变,在 \(i>n\) 时表示第一步被钦定选 \(bs\),\(j>n\) 时也类似。

在 \(h=bs\) 的时候开始转移 \(dp_{h,i+n,*}\),\(h=bt\) 时开始转移 \(dp_{h,*,i+n}\) 即可

Code Display
const int N=70;
int n,m,Q;
int dp[N][N<<1][N<<1],lef[N<<1],rig[N<<1];
char str[N][N];
bool G[N][N];
int s[N],t[N],bs[N],bt[N];
signed main(){
	freopen("cow.in","r",stdin);
	freopen("cow.out","w",stdout);
	n=read(); m=read(); Q=read();
	for(int i=1;i<=n;++i){
		scanf("%s",str[i]+1);
		for(int j=1;j<=n;++j) G[i][j]=str[i][j]-'0';
	}
	for(int i=1;i<=Q;++i){
		bs[i]=read(); s[i]=read();
		bt[i]=read(); t[i]=read();
	}
	for(int h=1;h<=m;++h){
		for(int i=1;i<=n+Q;++i){
			for(int j=1;j<=n+Q;++j) dp[h][i][j]=dp[h-1][i][j];
		}
		for(int k=1;k<=n;++k){
			for(int i=1;i<=n;++i) lef[i]=rig[i]=0;
			lef[k]=rig[k]=1;
			for(int i=1;i<=Q;++i){
				lef[i+n]=bs[i]==h&&s[i]==k;
				rig[i+n]=bt[i]==h&&t[i]==k;
			}
			for(int i=1;i<=n+Q;++i){
				for(int j=1;j<=n;++j){
					if(G[j][k]) ckadd(lef[i],dp[h-1][i][j]);
				}
			}
			for(int i=1;i<=n;++i){
				for(int j=1;j<=n+Q;++j){
					if(G[k][i]) ckadd(rig[j],dp[h-1][i][j]);
				}
			}
			for(int i=1;i<=n+Q;++i){
				for(int j=1;j<=n+Q;++j){
					ckadd(dp[h][i][j],mul(lef[i],rig[j]));
				}
			}
		}
	}
	for(int i=1;i<=Q;++i) print(dp[m][i+n][i+n]);
	return 0;
}

青蛙题

注意到一个区间的子区间颜色数最大不会超过其本身的颜色数,那么在扫描线过程中维护每个左端点到当前右端点的最大颜色数以及达到该颜色的最短区间

右端点右移到 \(r+1\) 时将最后一次该颜色出现位置到 \(r+1\) 的颜色数加一并更改最短区间长度为 \(r-i+1\)

把暴力的过程用线段树维护,每个叶子维护二元组 \((num,len)\) 表示颜色数和最短区间,区间维护 \(num\) 最大的二元组中 \(len\) 最小的一个

此时并不能支持区间修改得到新的 \(len\),但是发现这就是找到最靠右的 \(num\) 最大的,那么再维护一下位置即可

Code Display
const int N=2e6+10;
int n,Q,a[N],ans[N],lst[N];
vector<pair<int,int> >qu[N];
struct Segment_Tree{
	pair<int,int> Mx[N<<2];
	int Rmx[N<<2],delt[N<<2],R[N<<2];
	inline void push_tag(int p,int d,int r){
		Mx[p]={Mx[p].fir+d,r-Rmx[p]+1};
		R[p]=r; delt[p]+=d;
	}
	inline void push_down(int p){
		if(delt[p]){
			push_tag(p<<1,delt[p],R[p]);
			push_tag(p<<1|1,delt[p],R[p]);
			delt[p]=0; 
			R[p]=0;
		}
		return ;
	}
	inline void push_up(int p){
		if(Mx[p<<1].fir>Mx[p<<1|1].fir){
			Rmx[p]=Rmx[p<<1];
			Mx[p]=Mx[p<<1];
		}else{
			Mx[p]=Mx[p<<1|1];
			if(Mx[p].fir==Mx[p<<1].fir) ckmin(Mx[p].sec,Mx[p<<1].sec);
			Rmx[p]=Rmx[p<<1|1];
		}
	}
	inline void insert(int st,int ed,int p=1,int l=1,int r=n){
		if(st<=l&&r<=ed){
			push_tag(p,1,ed);
			return ;
		}
		int mid=(l+r)>>1; push_down(p);
		if(st<=mid) insert(st,ed,p<<1,l,mid);
		if(ed>mid) insert(st,ed,p<<1|1,mid+1,r);
		return push_up(p);
	}
	inline pair<int,int> query(int st,int ed,int p=1,int l=1,int r=n){
		if(st<=l&&r<=ed) return Mx[p];
		int mid=(l+r)>>1; push_down(p);
		if(ed<=mid) return query(st,ed,p<<1,l,mid);
		if(st>mid) return query(st,ed,p<<1|1,mid+1,r);
		pair<int,int> lres=query(st,ed,p<<1,l,mid);
		pair<int,int> rres=query(st,ed,p<<1|1,mid+1,r);
		if(lres.fir^rres.fir) return lres.fir>rres.fir?lres:rres;
		return {lres.fir,min(lres.sec,rres.sec)};
	}
	inline void build(int p,int l,int r){
		Rmx[p]=r; Mx[p].sec=1;
		if(l==r) return ;
		int mid=(l+r)>>1;
		build(p<<1,l,mid); build(p<<1|1,mid+1,r);
	}
}T;
int main(){
	freopen("frog.in","r",stdin);
	freopen("frog.out","w",stdout);
	n=read();
	rep(i,1,n) a[i]=read();
	Q=read();
	for(int i=1;i<=Q;++i){
		int l=read(),r=read();
		qu[r].emplace_back(l,i);
	}
	T.build(1,1,n);
	for(int i=1;i<=n;++i){
		T.insert(lst[a[i]]+1,i);
		for(auto q:qu[i]) ans[q.sec]=T.query(q.fir,i).sec;
		lst[a[i]]=i;
	}
	for(int i=1;i<=Q;++i) print(ans[i]);
	return 0;
}

标签:13,07,int,mxtop,cros,pos,2022,区间,Mx
来源: https://www.cnblogs.com/yspm/p/TestReview20220713.html