题意:一个二维矩阵,最开始每个点都独自属于自身连通块,接下来有两种操作,一种是将行号属于 i ∈ [ a , b ] i\in[a,b] i∈[a,b]的画一条横线(即 1 < = j < m , ( i , j ) 连 接 ( i , j + 1 ) 1<=j
第二种是画竖线,让你在每次操作之后输出连通块的个数。
假设矩阵大小为 n ∗ m n*m n∗m,已经画了的横线的个数为x,竖线的个数为y。
所以说只要可以维护横线,竖线的数量这道题就可以解决了。
如果直接开一个长度为 1 0 9 10^9 109的线段树(维护横线),是不是只要将线段树对应的端点看做对应的区间,如果将行号属于 [ l , r ] [l,r] [l,r]范围的画横线,那线段树上该端点处横线的数量就会变成 r − l + 1 r-l+1 r−l+1,同时注意到该题并不需要查询操作,每次只是看线段树的首节点处线段的数量,故不需要标记下传,我们不关心更低层次的数量。
但是肯定开不了这么大的空间,并且注意到值域区间虽然很大,但操作次数很少,所以可以动态开点线段树优化空间,而且需要注意的是在更新的时候如果该线段树上该点代表的区间已经全部画线了则可直接返回,避免造成空间的多开销,实测不加该话会MLE。
也可离散化。
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
struct Tree{
int lc,rc,sum;
}a[maxn*28];//线段树内存池;
int tot;
int build(){
int k=++tot;
a[k].lc=a[k].rc=a[k].sum=0;
return k;
}
void insert(int p,int l,int r,int L,int R){
if(a[p].sum==r-l+1) return ;
if(l>=L&&r<=R){
a[p].sum=r-l+1;
return ;
}
int mid=(l+r)>>1;
if(L<=mid){
if(!a[p].lc) a[p].lc=build();
insert(a[p].lc,l,mid,L,R);
}
if(R>mid){
if(!a[p].rc) a[p].rc=build();
insert(a[p].rc,mid+1,r,L,R);
}
a[p].sum=a[a[p].lc].sum+a[a[p].rc].sum;
}
int main(){
int n,m,q,rtrow,rtcal,id,l,r;
ll res;
while(scanf("%d%d%d",&n,&m,&q)!=EOF){
tot=0;
rtrow=build();
rtcal=build();
while(q--){
scanf("%d%d%d",&id,&l,&r);
if(id==1) insert(rtrow,1,n,l,r);
else insert(rtcal,1,m,l,r);
res=0;
if(a[rtrow].sum==0) res=(m-a[rtcal].sum)*1LL*n+a[rtcal].sum;
else if(a[rtcal].sum==0) res=(n-a[rtrow].sum)*1LL*m+a[rtrow].sum;
else{
res=n*1LL*m-a[rtrow].sum*1LL*m-a[rtcal].sum*1LL*n+a[rtrow].sum*1LL*a[rtcal].sum+1;
}
printf("%lld\n",res);
}
}
return 0;
}
离散化:线段树节点维护左闭右开区间
#include
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
int cc[maxn<<1],rr[maxn<<1];
int nn,mm;
void quchong(int id1,int id2){
sort(rr+1,rr+1+id1);
sort(cc+1,cc+1+id2);
nn=unique(rr+1,rr+1+id1)-rr-1;
mm=unique(cc+1,cc+1+id2)-cc-1;
}
int getr(int x){ return lower_bound(rr+1,rr+1+nn,x)-rr; }
int getc(int x){ return lower_bound(cc+1,cc+1+mm,x)-cc; }
int row[maxn<<3|1],cal[maxn<<3|1];
bool lr[maxn<<3|1],lc[maxn<<3|1];
void build(int l,int r,int k,int type){
if(l>r) return ;
if(type) row[k]=lr[k]=0;
else cal[k]=lc[k]=0;
if(l+1==r) return ;
int mid=(l+r)>>1;
build(l,mid,k<<1,type);
build(mid,r,k<<1|1,type);
}
void pushdown(int l,int r,int k,int type){
if(l+1==r) return ;
int mid=(l+r)>>1;
if(type){
if(lr[k]){
lr[k<<1]=lr[k<<1|1]=1;
row[k<<1]=rr[mid]-rr[l];
row[k<<1|1]=rr[r]-rr[mid];
lr[k]=0;
}
}
else{
if(lc[k]){
lc[k<<1]=lc[k<<1|1]=1;
cal[k<<1]=cc[mid]-cc[l];
cal[k<<1|1]=cc[r]-cc[mid];
lc[k]=0;
}
}
}
void updata(int l,int r,int k,int L,int R,int type){
if(l>r) return ;
if(l>=L&&r<=R){
if(type){
lr[k]=1;
row[k]=rr[r]-rr[l];
}
else{
lc[k]=1;
cal[k]=cc[r]-cc[l];
}
return ;
}
if(l+1==r) return ;
int mid=(l+r)>>1;
pushdown(l,r,k,type);
if(R<=mid) updata(l,mid,k<<1,L,R,type);
else if(L>=mid) updata(mid,r,k<<1|1,L,R,type);
else{
updata(l,mid,k<<1,L,R,type);
updata(mid,r,k<<1|1,L,R,type);
}
if(type) row[k]=row[k<<1]+row[k<<1|1];
else cal[k]=cal[k<<1]+cal[k<<1|1];
}
int op[maxn],L[maxn],R[maxn];
int main(){
int n,m,q,id1,id2,l,r;
while(scanf("%d%d%d",&n,&m,&q)!=EOF){
id1=0,id2=0;
for(int i=1;i<=q;++i){
scanf("%d%d%d",&op[i],&L[i],&R[i]);
++R[i];
if(op[i]==1) rr[++id1]=L[i],rr[++id1]=R[i];
else cc[++id2]=L[i],cc[++id2]=R[i];
}
//rr[++id1]=n+1;
//cc[++id2]=m+1;
quchong(id1,id2);
build(1,nn,1,1);
build(1,mm,1,0);
//nn 行 mm 列;
for(int i=1;i<=q;++i){
if(op[i]==1){
l=getr(L[i]),r=getr(R[i]);
updata(1,nn,1,l,r,1);
}
else{
l=getc(L[i]),r=getc(R[i]);
updata(1,mm,1,l,r,0);
}
int rrr=row[1],ccc=cal[1];
ll res=0;
if(!ccc) res=(n-rrr)*1LL*m+rrr;
else if(!rrr) res=(m-ccc)*1LL*n+ccc;
else res=n*1LL*m-n*1LL*ccc-m*1LL*rrr+ccc*1LL*rrr+1;
printf("%lld\n",res);
}
build(1,nn,1,1);
build(1,mm,1,0);
}
return 0;
}