bzoj1062【Noi2008】糖果雨
首先给出的颜色没有用。
估计要用数据结构。而线段难以维护。
考虑把线段变成点
T是单增的。
所以询问的时候,存在的线段都可能贡献答案。
那些线段的位置如果可以统一一下就好了。
发现线段2*len一个循环
思路:把所有的线段移动到l=0
或者说,考虑l=0的时候,时间是多少
横坐标:x=(ti-d*l)%(2*len)(这个时间仅仅为了相对关系表示方便,实际上,这个线段可能根本不会在这个时间出现,不过没有关系)
纵坐标:y=r-l
这样的好处是,线段都是从l=0向右移动了
实际上坐标是多少,就意味着距离0还有多远
(看上面博客有图)
然后对于询问的t
分成一个图形,计算点的个数
转化为平行四边形
转化为矩形
二维树状数组维护
为了避免讨论
可以直接分成四块。不合法的直接面积返回0
注意时间轴的下标是从[0,2*len-1]的
所以树状数组还要集体平移1
细节较多。边界有些恶心
r==len的时候还要特别防止一个点统计两遍。
代码:
#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=200000+5; const int M=2020; int n,len; int mod; struct node{ int x,y1,y2; }p[N]; int cnt; int co[1000000+5]; struct at{ int f[2*M][4*M]; void add(int x,int y,int c){ //swap(x,y); while(x<2010){ int tmp=y; while(tmp<4010){ f[x][tmp]+=c; tmp+=tmp&(-tmp); } x+=x&(-x); } } int query(int x,int y){ //swap(x,y); if(x<0||y<0) return 0; ++x,++y; if(x>=2*len) x=2*len; if(y>=4*len) y=4*len; int ret=0; // cout<<" x y "<<x<<" "<<y<<endl; while(x){ int tmp=y; while(tmp){ ret+=f[x][tmp]; tmp-=tmp&(-tmp); } x-=x&(-x); } return ret; } }t1,t2; int get(int x1,int y1,int x2,int y2,int c){ // cout<<x1<<" "<<y1<<" "<<x2<<" "<<y2<<" "<<c-1<<endl; if(c==1){ int ret=t1.query(x2,y2)+t1.query(x1-1,y1-1)-t1.query(x1-1,y2)-t1.query(x2,y1-1); // cout<<" ret "<<ret<<endl; return ret; }else{ int ret=t2.query(x2,y2)+t2.query(x1-1,y1-1)-t2.query(x1-1,y2)-t2.query(x2,y1-1); // cout<<" ret "<<ret<<endl; return ret; } } int sol(int t,int l,int r){ int d=(r==len); return get(t,t+l,t+r,4*len,1)+get(0,t+l-mod,t+r-mod-d,4*len,1) +get(t-r,l-t+mod,t-1,4*len,2)+get(t-r+mod+d,l-t,mod-1,4*len,2); } int main(){ rd(n);rd(len); int op,t,c,l,r,d; mod=2*len; while(n--){ rd(op); if(op==1){ rd(t);rd(c);rd(l);rd(r);rd(d); ++cnt; p[cnt].x=(t-d*l+mod)%mod; p[cnt].y1=(r-l+p[cnt].x); p[cnt].y2=(r-l-p[cnt].x+mod); // cout<<" point "<<p[cnt].x<<" "<<p[cnt].y1<<" || "<<p[cnt].y2<<endl; co[c]=cnt; t1.add(p[cnt].x+1,p[cnt].y1+1,1); t2.add(p[cnt].x+1,p[cnt].y2+1,1); }else if(op==2){ rd(t);rd(l);rd(r); t%=mod; printf("%d ",sol(t,l,r)); }else{ rd(t);rd(c); t1.add(p[co[c]].x+1,p[co[c]].y1+1,-1); t2.add(p[co[c]].x+1,p[co[c]].y2+1,-1); co[c]=0; } } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/1/8 14:12:04 */
转化还是鬼斧神工
方向就是抓住时间单增,统一出发位置考虑,mod意义下处理,线段变成点,方便维护。
不规则图形转化,坐标翻转。
同时避免讨论的小trick也不错。(这还是对水平要求比较高的)