白云一片去悠悠

问题:
给定一个\(n\times m\)的矩阵,给定的方式为给定一个长度为\(n\)的数组\(a\)和一个长度为\(m\)的数组\(b\),矩阵中的权值\(w_{i,j}=a_i+b_j\),再给定一个正整数\(x\),问权值小于等于\(x\)的点能组成多少四连通块。
思路:
首先解决四联通块计数问题有一个\(trick\),就是可以用四连通块中的一个点代表这个四连通块,这个问题中我们可以用权值最小的点代替,如果有多个最小就去左上角的那个。但是不会有多个左上角吗?
如:

其满足
\(\left\{\begin{matrix} a_{i_1}+b_{j_1}=a_{i_2}+b_{j_2}\\ a_{i_1}+b_{j_2}>a_{i_1}+b_{j_1}\\a_{i_2}+b_{j_1}>a_{i_1}+b_{j_1}\end{matrix}\right.\)
矛盾,所以这种情况不成立。
这样我只需要找出可作为代表点的个数就可以了。
要成为代表点首先要满足\(a_i+b_j\le x\),并且要满足如下图:

\((i,j_{1}^{`})\)\((i_{1}^{`},j)\)\((i,j)\)的向左和向上的最近权值小于等于\((i,j)\)的点。
\((i,j_{2}^{`})\)\((i_{2}^{`},j)\)\((i,j)\)的向右和向下的最近权值小于\((i,j)\)的点。
要使\((i,j)\)为代表点必须不能让它与这四个点联通,即定义一个\(f\)数组,\(f_i=\min(\max(a_{i_{1}^{`}+1\sim i-1}),\max(a_{i+1\sim i_{2}^{`}-1}))\),以及一个\(g\)数组,\(g_j=\min(\max(b_{j_{1}^{`}+1\sim j-1}),\max(b_{j+1\sim j_{2}^{`}-1}))\),要满足
\(\left\{\begin{matrix} a_i+g_j>x\\f_i+b_j>x \end{matrix}\right.\)
所以总的来说需要满足三个条件:
\(\left\{\begin{matrix}a_i+g_j>x \\ f_i+b_j>x\\ a_i+b_j\le x\end{matrix}\right.\)
最后让\(a\)数组单调,且用主席树维护\(f\)数组,就可以做出。
代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#define INF 200010
using namespace std;
int kd(){
	int x=0,f=1;
	char a=getchar();
	while(a<'0'||a>'9'){
		if(a=='-'){
			f=-1;
		}
		a=getchar();
	}
	while(a>='0'&&a<='9'){
		x=x*10+a-'0';
		a=getchar();
	}
	return x*f;
}
int n,m,x;
struct node{
	int zhi;
	int f;
}a[200010],b[200010];
bool cmp(node x,node y){
	return x.zhi<y.zhi;
}
int zhan[200010],top;
struct nod{
	int l,r;
	int ls,rs;
	int sum;
}tree[6000010];
int cnt=0;
int tou[200010];
void build(int i,int l,int r){
	tree[i].l=l;
	tree[i].r=r;
	tree[i].sum=0;
	if(l==r){
		return ;
	}
	int mid=(l+r)/2;
	tree[i].ls=++cnt;
	build(cnt,l,mid);
	tree[i].rs=++cnt;
	build(cnt,mid+1,r);
}
void buil(int i,int j,int p){
	tree[i].sum++;
	if(tree[i].l==tree[i].r){
		return ;
	}
	if(tree[tree[i].ls].r>=p){
		tree[i].ls=++cnt;
		tree[cnt]=tree[tree[j].ls];
		buil(cnt,tree[j].ls,p);
	}
	else{
		tree[i].rs=++cnt;
		tree[cnt]=tree[tree[j].rs];
		buil(cnt,tree[j].rs,p);
	}
}
int search(int i,int j,int l,int r){
	if(tree[i].l>=l&&tree[i].r<=r){
		return tree[i].sum-tree[j].sum;
	}
	int s=0;
	if(tree[tree[i].ls].r>=l){
		s+=search(tree[i].ls,tree[j].ls,l,r);
	}
	if(tree[tree[i].rs].l<=r){
		s+=search(tree[i].rs,tree[j].rs,l,r);
	}
	return s;
}
long long ans;
int main(){
	cin>>n>>m>>x;
	for(int i=1;i<=n;i++){
		a[i].zhi=kd();
	}
	top=0;
	for(int i=1;i<=n;i++){
		int maxn=0;
		while(top>0&&a[zhan[top]].zhi>a[i].zhi){
			int cun=a[zhan[top]].f;
			a[zhan[top]].f=min(a[zhan[top]].f,maxn);
			maxn=max(maxn,max(cun,a[zhan[top]].zhi));
			top--;
		}
		if(top==0){
			maxn=INF;
		}
		a[i].f=maxn;
		zhan[++top]=i;
	}
	for(int i=1;i<=m;i++){
		b[i].zhi=kd();
	}
	top=0;
	for(int i=1;i<=m;i++){
		int maxn=0;
		while(top>0&&b[zhan[top]].zhi>b[i].zhi){
			int cun=b[zhan[top]].f;
			b[zhan[top]].f=min(b[zhan[top]].f,maxn);
			maxn=max(maxn,max(cun,b[zhan[top]].zhi));
			top--;
		}
		if(top==0){
			maxn=INF;
		}
		b[i].f=maxn;
		zhan[++top]=i;
	}
	sort(a+1,a+n+1,cmp);
	tou[0]=++cnt;
	build(1,1,200000);
	for(int i=1;i<=n;i++){
		tou[i]=++cnt;
		tree[cnt]=tree[tou[i-1]];
		buil(tou[i],tou[i-1],a[i].f);
	}
	for(int i=1;i<=m;i++){
		int l=1,r=n;
		while(l<r){
			int mid=(l+r)/2;
			if(a[mid].zhi+b[i].f>x){
				r=mid;
			} 
			else{
				l=mid+1;
			}
		}
		int ll=l;
		if(a[n].zhi+b[i].f<=x){
			ll=n+1;
		}
		l=1,r=n;
		while(l<r){
			int mid=(l+r+1)/2;
			if(a[mid].zhi+b[i].zhi<=x){
				l=mid;
			}
			else{
				r=mid-1;
			}
		}
		int rr=l;
		if(a[1].zhi+b[i].zhi>x){
			rr=0;
		}
		if(ll<=rr){
			ans+=search(tou[rr],tou[ll-1],max(x-b[i].zhi+1,1),200000);
		}
	}
	cout<<ans;
	return 0;
}
posted @ 2023-11-29 08:46  zzzzzz2  阅读(48)  评论(0)    收藏  举报