【[CTSC2015]葱】可持久化替罪羊树维护KD-TREE乱搞
总感觉KDTREE的很多骚操作本质都是A*算法,感觉随便怎么都可以卡orz.....玄学orz...
luogu4509
H【CTSC2015 Day1】葱 | ||
|
问题描述
小葱和小绪是一对好朋友,自从小葱11连出了1UR2SR之后,小绪就觉得小葱的人品特别好,于是小绪给小葱出了一道题来测试小葱的人品。
小绪首先在平面上画了N个点,分别是P1,P2,...,PN。小绪把这N个点顺次相连,即连接(P1,P2),(P2,P3),...,(PN-1,PN),得到N-1条线段。
之后小绪每次在平面上画出一条直线,然后问小葱这条直线与多少条线段相交。特别的,在线段端点处相交算作相交,直线完全覆盖线段时也算作相交。
这样的问题自然难不倒小葱,小葱只需要凭自己的人品用直觉就能给出正确的答案。
小绪想测试小葱的人品究竟有多好,于是他加大了问题的难度:
除了每次询问以外,小绪会不时地讲一个新的点P插入到Pi和Pi+1之间,然后按照顺序对所有的点重新标记下标,即在Pi之后的点的下标会依次增加,而点P会变成新的点Pi+1。特别的,点P也可以插入到第一个点之前或最后一个点之后。
人品超级好的小葱依旧能够轻松的给出答案,于是小绪又进一步提高了难度:
每次插入或提问之后,小绪都将操作后的所有线段记录了下来,称作一个历史版本。历史版本T表示在第T次操作后得到的历史版本。
插入新点的操作改为了在某一个历史版本T的基础上,插入一个点P,并得到一个新的历史版本。
小绪对小葱的提问改为了对于一个历史版本T,给出一条直线,询问这条直线会与多少条线段相交。
小葱虽然人品很好,但面对这样的问题却也束手无策了,他只好找到来参加CTSC的你,请你来帮他解决这个问题。
输入格式
/输入文件shallot.in。/
第一行两个整数N,M,C『注:原文如此』,表示一开始的点数和总共的操作数,以及数据是否加密。如果C=1,那么代表数据被加密过,每次询问操作中的X0,Y0,X,Y以及插入操作中的X,Y都是被加密过的数据,你需要将它们异或last_ans从而得到正确的数据,其中last_ans是上一次询问的答案,刚开始last_ans=0。
接下来N行每行两个整数,其中第i行的两个整数表示Pi的横坐标和纵坐标。
接下来M行,表示小绪的M次操作,其中第i行(从1开始标号)操作后得到的结果为历史版本i。
对于每次操作,首先会有一个字母代表小绪的这次操作的操作类型。
如果这个字母是'H',代表本次操作为一次询问操作。接下来会有五个整数T,X0,Y0,X,Y,代表在历史版本T的情况下,小绪给出一条经过(X0,Y0),方向为(X,Y)的直线,小葱要回答出它会和多少条直线相交。
如果这个字母是'Z',代表本次操作为一次插入操作。接下来会有四个数T,i,X,Y,代表小绪在历史版本T的基础上,在Pi后面插入了一个坐标为(X,Y)的点。特别地,如果i=0,表示该点在P1之前。
输出格式
/输出文件sallot.out。/
要求对每一次询问操作,输出一行一个整数代表小葱应该回答的正确答案。
样例输入 1
2 3 0
0 0
1 1
H 0 1 0 -1 1
H 1 0 1 1 1
H 2 -1 -1 1 1
样例输出 1
1
0
1
样例输入 2
2 3 0
0 0
2 2
Z 0 2 0 2
H 0 0 1 1 1
H 1 0 1 1 1
样例输出 2
0
2
提示
样例解释1:
对于第三次村问,直线完全覆盖了线段,小绪会认为这也算相交。
数据规模和约定:
保证每次询问操作的T一定小于等于当前操作的数量,所有输入数据均为整数。
有以下4类特殊数据,它们两两没有交集:
1.对于10%的数据,保证1<=N,M<=1000;
2.对于15%的数据,保证对于第i次操作,T=i-1;
3.对于15%的数据,保证C=0且不存在修改操作;
4.对于15%的数据,对于询问操作,保证Y=0(加密过的数据指解密后的Y),即给出的直线平行于x轴。
以上数据还保证1<=N,M<=5104。
对于100%的数据,保证1<=N,M<=105,所有的坐标范围在[-108,108]内,且每组数据中所有询问的答案总和不超过106,插入操作的次数不会超过5104。注意这些线段可能会互相相交。
对于平面上的判交问题,我们可以很容易得想到利用KD-tree来解决。如果我们按照顺序构出一颗平衡树,并且同时每个节点包含了一个矩形。之后我们发现,对于所有的线段点对就是一个点和他的前驱所组成的所有线段。但是我们发现对每个点都找一次前驱会相当的复杂。但是我们发现对于这样一个点对,对于深度较浅的那个点来说就是要么是右儿子的最左点和或者是左儿子的最右点,如果对每个点都这样考虑之后,我们发现不重不漏的考虑完了所有线段对。
那么对于原题的查询,一条直线,我们只需要判断直线是否与点代表矩形判交,以及搜到某个点的时候,判定一下上面所说的两个点对代表线段与直线是否有交,而对于插入就是简单的平衡树插入,而可持久化,我们写一颗可持久化替罪羊树就OK了。
由于答案总和不超过106,插入操作的次数不会超过5104,所以不会被卡掉。
code:
#include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define int long long using namespace std; typedef long long ll; const int maxn = 100005; int n,m,type; struct node{ node *ls,*rs,*lmax,*rmax; int siz; int x,y; int sx[2],sy[2];int xds; }z[maxn*60],*nul,*tl,*rt[maxn],**RT; int ZX,ZY,FX,FY; ll A,B,C; ll calc(int x,int y) { return 1ll*A*x + 1ll*B*y + C; } bool che(int ax,int ay,int bx,int by) { ll f1 = calc(ax,ay) ,f2 = calc(bx,by); if(f1>f2) swap(f1,f2); return (f1<=0&&f2>=0); } struct dd { int x,y; }oo[maxn]; int top; node* nwnode(dd &tt) { node* p = ++tl; p->ls = p->rs = nul; p->siz = 1; p->x = p->sx[0] = p->sx[1] = tt.x; p->y = p->sy[0] = p->sy[1] = tt.y; p->lmax = p->rmax = p; p->xds = 0; return p; } void upd(node *&p) { p->siz = 1; p->xds = 0; if(p->ls!=nul) { p->sx[0] = min(p->sx[0],p->ls->sx[0]); p->sx[1] = max(p->sx[1],p->ls->sx[1]); p->sy[0] = min(p->sy[0],p->ls->sy[0]); p->sy[1] = max(p->sy[1],p->ls->sy[1]); p->lmax = p->ls->lmax; p->xds += p->ls->xds + 1; p->siz += p->ls->siz; } if(p->rs!=nul) { p->sx[0] = min(p->sx[0],p->rs->sx[0]); p->sx[1] = max(p->sx[1],p->rs->sx[1]); p->sy[0] = min(p->sy[0],p->rs->sy[0]); p->sy[1] = max(p->sy[1],p->rs->sy[1]); p->rmax = p->rs->rmax; p->xds += p->rs->xds + 1; p->siz += p->rs->siz; } } void makekd(node *&p,int l,int r) { int mid = (l+r)>>1; p = nwnode(oo[mid]); if(l<mid) makekd(p->ls,l,mid-1); if(mid<r) makekd(p->rs,mid+1,r); upd(p); } bool isbad(node *&p) { if(p->ls->siz*5 > p->siz*4 || p->rs->siz*5 > p->siz*4 ) return 1; return 0; } void tra(node *&p) { if(p==nul) return; tra(p->ls); oo[++top] = (dd) {p->x,p->y}; tra(p->rs); } void rebuild() { if(*RT==nul) return; top = 0; tra(*RT); if(top>0)makekd(*RT,1,top); else *RT = nul; RT = &nul; } void init() { nul = tl = z; nul->ls = nul->rs = nul; for(int i=0;i<=m;i++) { rt[i] = nul; } RT = &nul; } void ins(node *&p,node *&las,int orz,int X,int Y) { if(las==nul){ dd owo = (dd){X,Y}; p = nwnode((owo)); return; } p = ++tl; *p = *las; if(p->ls->siz>=orz) ins(p->ls,las->ls,orz,X,Y); else { orz -= p->ls->siz+1; ins(p->rs,las->rs,orz,X,Y); } upd(p); if(isbad(p)) RT = &p; } int query(node *&p) { if(p==nul) return 0; int sm = 0; if(p->ls!=nul) sm += che(p->x,p->y,p->ls->rmax->x,p->ls->rmax->y); if(p->rs!=nul) sm += che(p->x,p->y,p->rs->lmax->x,p->rs->lmax->y); int f0 = calc(p->sx[0],p->sy[0]); int f1 = calc(p->sx[0],p->sy[1]); int f2 = calc(p->sx[1],p->sy[0]); int f3 = calc(p->sx[1],p->sy[1]); if( (f0<0&&f1<0&&f2<0&&f3<0)||(f0>0&&f1>0&&f2>0&&f3>0) ) return sm; return query(p->ls) + query(p->rs) + sm; } main() { init(); scanf("%lld%lld%lld",&n,&m,&type); for(int i=1;i<=n;i++) { scanf("%lld%lld",&oo[i].x,&oo[i].y); } makekd(rt[0],1,n); int lastans = 0; char ss[2]; for(int i=1;i<=m;i++) { scanf("%s",&ss[0]); if(ss[0]=='H') { int T; scanf("%lld%lld%lld%lld%lld",&T,&ZX,&ZY,&FX,&FY); if(type) { ZX^=lastans; ZY^=lastans; FX^=lastans; FY^=lastans; } A = -FY; B = FX; C = - 1ll*A*ZX - 1ll*ZY*B; rt[i] = rt[T]; lastans = query(rt[i]); printf("%lld\n",lastans); } else { int T,orz,X,Y; scanf("%lld%lld%lld%lld",&T,&orz,&X,&Y); if(type)X^=lastans,Y^=lastans; ins(rt[i],rt[T],orz,X,Y); if(*RT!=nul) rebuild(); } } }