7.22NOIP模拟赛

终于场切了一道

A

中文题面

image

开两个线段树乱搞

如果一个数是区间中每一个数的约数,那么它一定是整个区间中的最小值不唯一
所以推出它是整个区间中所有数的最大公约数(gcd)

所以只要开线段树维护区间最小值和最小值的数量,区间gcd
每次判断该区间的最小值是否等于其gcd,若相等,输出最小值的数量,否则输出0

#include<bits/stdc++.h>
#define fst first
#define sec second
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef pair<int,int> auther;
const int maxn=5e5+5;
const int inf=2e9+1;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int mini(int x,int y){
	return x<y ? x : y;
}
int t1[maxn<<2],t2[maxn<<2];//t1区间最小值,t2区间gcd
int p[maxn<<2];//p区间最小值的个数
int a[maxn];
int n,q;
//线段树1,维护区间最小值和其数量
void push_up1(int u){
	t1[u]=mini(t1[u<<1],t1[u<<1|1]),p[u]=0;
	if(t1[u]==t1[u<<1]) p[u]+=p[u<<1];
	if(t1[u]==t1[u<<1|1]) p[u]+=p[u<<1|1];
}
void build1(int l=1,int r=n,int u=1){
	if(l==r){
		t1[u]=a[l];
		p[u]=1;
		return;
	}
	int mid=(l+r)>>1;
	build1(l,mid,u<<1),build1(mid+1,r,u<<1|1);
	push_up1(u);
}
void update1(int x,int p,int l=1,int r=n,int u=1){
	if(r<x||l>x) return;
	if(l==r){
		t1[u]=p;
		return;
	}
	int mid=(l+r)>>1;
	update1(x,p,l,mid,u<<1),update1(x,p,mid+1,r,u<<1|1);
	push_up1(u);
}
auther query1(int L,int R,int l=1,int r=n,int u=1){
	if(r<L||l>R) return make_pair(inf,0);
	if(r<=R&&l>=L) return make_pair(t1[u],p[u]);
	int mid=(l+r)>>1;
	auther i=query1(L,R,l,mid,u<<1),j=query1(L,R,mid+1,r,u<<1|1);
	if(i.fst<j.fst) return i;
	else if(i.fst>j.fst) return j;
	else return make_pair(i.fst,i.sec+j.sec);
}


//线段树2,维护区间gcd
void push_up2(int u){
	t2[u]=__gcd(t2[u<<1],t2[u<<1|1]);
}
void build2(int l=1,int r=n,int u=1){
	if(l==r){
		t2[u]=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build2(l,mid,u<<1),build2(mid+1,r,u<<1|1);
	push_up2(u);
}
void update2(int x,int p,int l=1,int r=n,int u=1){
	if(r<x||l>x) return;
	if(l==r){
		t2[u]=p;
		return;
	}
	int mid=(l+r)>>1;
	update2(x,p,l,mid,u<<1),update2(x,p,mid+1,r,u<<1|1);
	push_up2(u);
}
int query2(int L,int R,int l=1,int r=n,int u=1){
	if(r<L||l>R) return -1;
	if(r<=R&&l>=L) return t2[u];
	int mid=(l+r)>>1;
	int ans;
	int bl=query2(L,R,l,mid,u<<1),br=query2(L,R,mid+1,r,u<<1|1);
	if(bl<0&&br<0) ans=-1;
	else if(bl<0) ans=br;
	else if(br<0) ans=bl;
	else if(bl>0&&br>0) ans=__gcd(bl,br);
	return ans;
}
int main(){
	//freopen("multiples.in","r",stdin);
	//freopen("multiples.out","w",stdout);
	read(n),read(q);
	for(int i=1;i<=n;i++) read(a[i]);
	build1(),build2();
	int op,l,r;
	while(q--){
		read(op),read(l),read(r);
		if(op==1){
			update1(l,r),update2(l,r);
		}
		else{
			auther j=query1(l,r);
			int k=query2(l,r);
			if(j.fst==k){
				printf("%d\n",j.sec);
			}
			else{
				printf("0\n");
			}
		}
	}
	return 0;
}

B

原题在这里

中文题面

image

考虑计算一个数字为结尾时的数量

若一个数字是结尾,则它一定是合法子串中的最大值,即不可能包含其他比它大的数字
所以可以处理出它左边第一个比它大的数,记为f[i]

那么开头怎么选取呢?一定是比它小的数,且区间内不可能有比它还小的数

前面处理左边第一个比它大的数用到了单调栈,这里的情况刚好也可以用单调栈解决

我们再维护一个单调递增(非严格)的栈,每有一个数插入,都要弹出栈顶比它大的所有数字
这也对应了若有一个较小的数排在较大的数前面后面较大的数是不能作为开头的,因为区间中有比它小的数
(此处较小较大形容数的相对大小关系)

因为不能作为开头的数一定在插入的过程中被弹掉了
所以我们要选的开头一定在当前的单调栈中,并且编号比f[i]

由于这里要用upper_bound寻找边界,所以手动模拟栈的实现

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e6+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int st[maxn];//手动模拟栈实现
int a[maxn],f[maxn];
int tp=0;//栈顶
int n;
int main(){
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++){
		while(tp&&a[st[tp]]<=a[i]) --tp;//维护一个严格单调递减的栈
		f[i]=st[tp];
		st[++tp]=i;
	}//计算每一个数左边第一个比它大的数
	tp=0;//清空栈
	LL ans=0;
	for(int i=1;i<=n;i++){
		while(tp&&a[st[tp]]>a[i]) --tp;
		st[++tp]=i;//维护一个非严格单调递增的栈
		int p=upper_bound(st+1,st+tp+1,f[i])-st;//限制查询范围
		//for(int i=p;i<=tp;i++) cout<<a[st[i]]<<' '<<a[i]<<endl;
        //这段代码可以输出每个合法区间
		ans+=tp-p+1;
	}
	printf("%lld",ans);
	return 0;
}

C

中文题面

image

转化问题

原问题等价于判断一个矩形中是否满足所有行中均有1或所有列中均有1

考虑离线+扫描线

例如扫行时,排序并遍历每个矩形的下边界,在扫到一个边界前预处理出这个边界前的所有点
在线段树上维护这些点的行坐标(每个点放入其列坐标的桶中),到一个边界时,看区域内的最小的行坐标

如果小于该矩形的上边界,则说明这个矩形有一些列上是没有点的

否则说明这个矩形的所有列上都有点,那么该询问的答案为YES

同理扫列,这里可以通过交换点和矩形的行列坐标再调用原先扫行的函数解决

#include<bits/stdc++.h>
#define fst first
#define sec second
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef pair<int,int> auther;
const int maxn=1e5+5;
const int inf=2e9+1;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
struct sq{
	int x1,y1,x2,y2,id;
};
bool cmp1(auther a,auther b){
	return a.fst<b.fst;
}
bool cmp2(sq a,sq b){
	return a.x2<b.x2;
}
int n,m,k,q;
int mx;
auther d[maxn];
sq s[maxn];
int t[maxn<<2];
void push_up(int u){
	t[u]=min(t[u<<1],t[u<<1|1]);
}
void update(int x,int p,int l=1,int r=mx,int u=1){
	if(r<x||l>x) return;
	if(l==r){
		t[u]=p;
		return;
	}
	int mid=(l+r)>>1;
	update(x,p,l,mid,u<<1),update(x,p,mid+1,r,u<<1|1);
	push_up(u);
}
int query(int L,int R,int l=1,int r=mx,int u=1){
	if(r<L||l>R) return inf;
	if(l>=L&&r<=R) return t[u];
	int mid=(l+r)>>1;
	return min(query(L,R,l,mid,u<<1),query(L,R,mid+1,r,u<<1|1));
}//一个用于维护区间最小值的线段树
bool ans[maxn];
void work(){
	sort(d+1,d+k+1,cmp1);
	sort(s+1,s+q+1,cmp2);
	int p=1;
	for(int i=1;i<=q;i++){
		while(p<=k&&d[p].fst<=s[i].x2){
			update(d[p].sec,d[p].fst);
			++p;
		}
		int que=query(s[i].y1,s[i].y2);
		if(query(s[i].y1,s[i].y2)>=s[i].x1){
			ans[s[i].id]=1;
		}
	}
}
void change(){
	for(int i=1;i<=k;i++) swap(d[i].fst,d[i].sec);
	for(int i=1;i<=q;i++){
		swap(s[i].x1,s[i].y1);
		swap(s[i].x2,s[i].y2);
	}
	memset(t,0,sizeof(t));
}//交换行列坐标扫列
int main(){
	//freopen("matrix.in","r",stdin);
	//freopen("matrix.out","w",stdout);
	read(n),read(m),read(k),read(q);
	mx=max(n,m);
	for(int i=1;i<=k;i++){
		read(d[i].fst),read(d[i].sec);
	}
	for(int i=1;i<=q;i++){
		read(s[i].x1),read(s[i].y1),read(s[i].x2),read(s[i].y2);
		s[i].id=i;
	}
	work();
	change();
	work();
	for(int i=1;i<=q;i++){
		if(ans[i]) printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}

D

中文题面

image

先放放肯定不是因为我不会凸包

posted @ 2025-07-22 20:37  huangems  阅读(11)  评论(0)    收藏  举报