P3875 [TJOI2010]被污染的河流
链接:Miku
-------------------------
我记得扫描线有一道更水的例题来
-------------------------
扫描线,顾名思义,我们做这道题的时候,就要是用一道线来扫描一样
这里有一堆矩阵,把每一个区间拆成上下两个线段,然后按照某一个端点排序,这道线就开始从头扫描
到了一个矩阵开始的线,我们就更新扫描线的长度,反之减少即可
两个线段之间的长度很明显就是区间距离*扫描线长度即可
-----------------------------
直接这么写只有30分,还是要用线段树维护的
线段树可以加快我们查询扫描线长度和更新扫描线长度的速度
------------------------------
这里的线段树我lazy记录的区间被修改次数,然后sum是区间长度(为1的数量),显然,根节点就是我们需要的,而且没必要写查询函数
但是我的做法继续操作的相对行,有减必然有长度一样的加,所以我的代码中不需要pushdown(加了还错,至少我是)
而且考虑到lazy和sum的区别,pushup会调用的更加频繁,甚至会pushup(叶子节点),然而叶子节点没有左右节点,要特判(也可能是我线段树写法的问题)
-------------------------------
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int x1,y1,x2,y2; long long ans; struct li{ int l; int r; long long h; int f; }line[200005]; int n; int p; int sum[4000005]; int lazy[4000005]; bool cmp(li x,li y){ return x.h<y.h; } void pushup(int x,int l,int r){ if(lazy[x]>=1)//如果被改了,肯定就是区间的长度了 sum[x]=r-l+1; else if(l!=r) sum[x]=sum[x<<1]+sum[x<<1|1];//考虑叶子节点 的问题 else sum[x]=0; } void update(int x,int l,int r,int L,int R,int d){ if(L<=l&&r<=R){ lazy[x]+=d; pushup(x,l,r);//冗长的特判不如加到一起 return ; } int mid=(l+r)>>1; if(L<=mid) update(x<<1,l,mid,L,R,d); if(R>mid) update(x<<1|1,mid+1,r,L,R,d); pushup(x,l,r); } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d%d%d%d",&x1,&y1,&x2,&y2);//非常奇特的输入 if(x1>x2) swap(x1,x2); if(y1>y2) swap(y1,y2); //f的值是1或-1可以省事 if(x1==x2){ p++;//自己造矩形 line[p].l=y1+1; line[p].r=y2; line[p].h=x1-1; line[p].f=1; p++; line[p].l=y1+1; line[p].r=y2; line[p].h=x1+1; line[p].f=-1; } else{ p++; line[p].l=y1; line[p].r=y1+1; line[p].h=x1; line[p].f=1; p++; line[p].l=y1; line[p].r=y1+1; line[p].h=x2; line[p].f=-1; } } sort(line+1,line+p+1,cmp); for(int i=1;i<=p;++i){ ans+=(line[i].h-line[i-1].h)*sum[1];//显然 update(1,0,1000005,line[i].l,line[i].r,line[i].f);//就在这省事 } cout<<ans; return 0; }