Grid
这是跟平时不太一样的线段树。
这道题要用是要线段树记录区间(线段),然后判断 当前所记录的 所有线段 总共覆盖的长度,也就是说,线段可能会重叠,重叠的区间长度不能重复计算,要计算实际得到的 所有线段的长度。
想想 怎么记录? 跟平时一样的话,我大概就是,把线段分为单个单个点,然后把区间标记为1,然后计算的时候就,到了标记为1的区间,就直接ans+=r-l+1,但是,因为是分散的区间线段,搜索时间会大于平时。有点不足,也不知道有没有别的做法。
但是,这道题还不能用这种做法,因为区间长度是1e9,需要离散。
所以看了别人的做法:离散询问出现的点的坐标,然后,线段是[l,r],把线段树的[l,r]的值置为h[r]-h[l],这个也不是我想说的重点。
但是,离散了之后,mid和mid+1的值就断开了,这样子 两个区间的值不就不连续了吗?会缺少[h[mid],h[mid+1]]的值。
所以看了别人的做法:线段树的左右子树的区间是[l,mid] [mid,r],或者 更准确的说是,传递到左右子树的离散数组区间是[ h[l],h[mid] ]和[ h[mid],h[r] ] ,这样子就能解决~...了吗?有个小细节忘了说 线段区间[val[l],val[r]]的长度并不是真实的h[r]-h[l],在记录r的时候 h[r]的值实际是val[r+1],这样才满足区间的真实长度(r-l+1)嘛。
想无,记下来。
代码:
#include#include #define debug printf("!"); using namespace std; typedef long long ll; const int maxn=1e5+50; const int mod=1e9+7; const int inf=0x3f3f3f3f; struct P{ int ly;ll sum; }tree1[maxn<<3],tree2[maxn<<3]; int h[maxn<<1],tot,nn,mm; int val(int v) { return lower_bound(h+1,h+1+tot,v)-h; } void down(int k,int L,int R,P tree[]) { int mid=(L+R)>>1; tree[k<<1].sum=h[mid]-h[L]; tree[k<<1|1].sum=h[R]-h[mid]; tree[k<<1].ly=1; tree[k<<1|1].ly=1; tree[k].ly=0; } void update(int k,int L,int R,int l,int r,P tree[]) { if(l==L&&R==r) { tree[k].sum=h[r]-h[l]; tree[k].ly=1; return ; } if(tree[k].ly)down(k,L,R,tree); int mid=(L+R)>>1; if(r<=mid)update(k<<1,L,mid,l,r,tree); else if(l>=mid)update(k<<1|1,mid,R,l,r,tree); else { update(k<<1,L,mid,l,mid,tree); update(k<<1|1,mid,R,mid,r,tree); } tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum; } struct Q{ int op,a,b; }p[maxn]; int main() { ll n,m; ll ans,t; int q,op,a,b; while(~scanf("%lld%lld%d",&n,&m,&q)) { tot=0; for(int i=1;i<=q;i++) { scanf("%d%d%d",&p[i].op,&p[i].a,&p[i].b); h[++tot]=p[i].a;h[++tot]=p[i].b+1; } h[++tot]=n+1;h[++tot]=m+1; sort(h+1,h+1+tot); tot=unique(h+1,h+1+tot)-h-1; nn=val(n+1);mm=val(m+1); for(int i=1;i<=tot*4;i++) { tree1[i].sum=tree1[i].ly=0; tree2[i].sum=tree2[i].ly=0; } for(int i=1;i<=q;i++) { a=val(p[i].a);b=val(p[i].b+1); if(p[i].op==1)update(1,1,nn,a,b,tree1); else update(1,1,mm,a,b,tree2); ans=(n*m)-(tree1[1].sum*m+tree2[1].sum*n)+(tree1[1].sum*tree2[1].sum); if(tree1[1].sum==0)ans+=tree2[1].sum; else if(tree2[1].sum==0)ans+=tree1[1].sum; else ans++; printf("%lld\n",ans); } } }
但是!!!但是,更好的做法是:动态开点线段树。
但是!!!但是,在牛客这样做会超内存。等国庆过后去别的oj补题。
#include#include #define debug printf("!"); using namespace std; typedef long long ll; const int maxn=1e5+5; const int mod=1e9+7; const int inf=0x3f3f3f3f; int sum1[maxn<<3],L1[maxn<<3],R1[maxn<<3],cnt1,T1; int sum2[maxn<<3],L2[maxn<<3],R2[maxn<<3],cnt2,T2; void init() { for(int i=0;i<=cnt1;i++)sum1[i]=L1[i]=R1[i]=0; for(int i=0;i<=cnt2;i++)sum2[i]=L2[i]=R2[i]=0; cnt1=cnt2=0; } void update1(int &rt,int l,int r,int tl,int tr) { if(!rt)rt=++cnt1; if(sum1[rt]==r-l+1)return; if(tl<=l&&r<=tr) { sum1[rt]=r-l+1;return; } int mid=(l+r)>>1; if(tl<=mid)update1(L1[rt],l,mid,tl,tr); if(tr>mid)update1(R1[rt],mid+1,r,tl,tr); sum1[rt]=sum1[L1[rt]]+sum1[R1[rt]]; } void update2(int &rt,int l,int r,int tl,int tr) { if(!rt)rt=++cnt2; if(sum2[rt]==r-l+1)return; if(tr<=l&&r<=tr) { sum2[rt]=r-l+1;return; } int mid=(l+r)>>1; if(tl<=mid)update2(L2[rt],l,mid,tl,tr); if(tr>mid)update2(R2[rt],mid+1,r,tl,tr); sum2[rt]=sum2[L2[rt]]+sum2[R2[rt]]; } int main() { ll n,m; ll ans,t; int q,op,a,b; while(~scanf("%lld%lld%d",&n,&m,&q)) { init(); while(q--) { scanf("%d%d%d",&op,&a,&b); if(op==1)update1(T1,1,n,a,b); else update2(T2,1,m,a,b); ans=n*m-((ll)sum1[1]*m+(ll)sum2[1]*n)+(ll)sum1[1]*(ll)sum2[1]; if(sum1[1]==0)ans+=sum2[1]; else if(sum2[1]==0)ans+=sum1[1]; else ans++; printf("%lld\n",ans); } } }