最近做的一份jsoi2011冬令营的卷子,瞬间被虐暴了。。。。。
其中以两道平衡树最为奇葩。
蒟蒻今天终于写完了两道平衡树——调了好长好长时间,高级数据结构能力太渣了。
--------------------------------------------华丽的分割线--------------------------------------------
(最近搞了个强大的截图工具)题面就不概述了,直接放图吧
一眼看上去就是splay吧。
读入后从最后一个询问倒序处理,删除变成添加,用启发式合并,修改变成复原,询问直接处理吧
然后蒟蒻令人捉急的代码能力就开始显现出来了,一调一下午。。。。。
主要是忘记了splay删除怎么写了。。。原来写了个好龊好龊的,现在好不容易想起来,这里补一个备忘
void del(int x,int tr){ Splay(x,0,tr); int pr=son[x][0], nx=son[x][1]; if (pr==0) { root[tr]=nx; fa[nx]=0; return; } if (nx==0) { root[tr]=pr; fa[pr]=0; return; } while (son[pr][1]) pr=son[pr][1]; while (son[nx][0]) nx=son[nx][0]; Splay(pr,0,tr); Splay(nx,pr,tr); son[nx][0]=0; Splay(nx,0,tr); }
#include <vector> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Maxn=30005, Maxm=100005, Maxq=500005; int son[Maxn][2],fa[Maxn],size[Maxn],root[Maxn],ft[Maxn]; int n,m,i,q,m1,m2,cnt,ans,tot,num[Maxq],data[Maxn]; bool v[Maxm]; char s[Maxq]; vector <int> e[Maxn]; struct EDGE { int x,y; bool operator <(const EDGE &a)const { return (x<a.x) || (x==a.x && y<a.y); } bool operator ==(const EDGE &a)const { return (x==a.x && y==a.y); } } edge[Maxm], g[Maxq], tmp; void rotate(int x,int f,int K){ if (fa[f]!=0){ if (son[fa[f]][0]==f) son[fa[f]][0]=x; else son[fa[f]][1]=x; } fa[x]=fa[f]; if (son[x][K^1]) fa[son[x][K^1]]=f; son[f][K]=son[x][K^1]; son[x][K^1]=f; fa[f]=x; size[f]=size[son[f][0]] +size[son[f][1]]+ 1; } void Splay(int x,int y,int tr){ int f,gf; while (fa[x]!=y){ f=fa[x]; gf=fa[f]; if (gf==y){ if (son[f][0]==x) rotate(x,f,0); if (son[f][1]==x) rotate(x,f,1); } else { if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0); if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1); if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1); if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0); } } if (y==0) root[tr]=x; size[x]=size[son[x][0]] +size[son[x][1]]+ 1; } void del(int x,int tr){ Splay(x,0,tr); int pr=son[x][0], nx=son[x][1]; if (pr==0) { root[tr]=nx; fa[nx]=0; return; } if (nx==0) { root[tr]=pr; fa[pr]=0; return; } while (son[pr][1]) pr=son[pr][1]; while (son[nx][0]) nx=son[nx][0]; Splay(pr,0,tr); Splay(nx,pr,tr); son[nx][0]=0; Splay(nx,0,tr); } void ins(int x,int tr){ son[x][0]= son[x][1]= fa[x]=0; int y=root[tr]; if (y==0){ root[tr]=x; size[x]=1; } else while (true){ if (data[x]>data[y]){ if (son[y][1]) y=son[y][1]; else {son[y][1]=x; fa[x]=y; break;} }else { if (son[y][0]) y=son[y][0]; else {son[y][0]=x; fa[x]=y; break;} } } Splay(x,0,tr); } int query(int x,int tr){ int ret=0; int y=root[tr]; while (y){ if (data[y]>=x){ ret=data[y]; y=son[y][0]; } else y=son[y][1]; } return ret; } void get_together(int x,int y){ ft[x]=y; while (e[x].size()){ int tmp=e[x].back(); e[x].pop_back(); e[y].push_back(tmp); son[tmp][0]=son[tmp][1]=0; fa[tmp]=0; ins(tmp,y); } } int get_ft(int x){ int xx=x, xxx; while (ft[xx]!=xx) xx=ft[xx]; while (x!=xx) xxx=x, x=ft[x], ft[xxx]=xx; return xx; } int main(){ freopen("boring.in","r",stdin); freopen("boring.out","w",stdout); scanf("%d%d%d",&n,&m,&q); for (i=1;i<=n;i++) scanf("%d",&data[i]); for (i=1;i<=m;i++){ scanf("%d%d\n",&edge[i].x,&edge[i].y); if (edge[i].x>edge[i].y) swap(edge[i].x,edge[i].y); } sort(edge+1,edge+m+1); for (i=1;i<=q;i++){ scanf("%c%d%d\n",&s[i],&g[i].x,&g[i].y); if (s[i]=='U') swap(data[g[i].x],g[i].y); tmp=g[i]; if (tmp.x>tmp.y) swap(tmp.x,tmp.y); if (s[i]!='E') continue; num[i]=lower_bound(edge+1,edge+m+1,tmp)-edge; if (edge[ num[i] ]==tmp){ while (edge[ num[i] ]==tmp && v[num[i]]) num[i]++; v[num[i]]=1; } else num[i]=0; } for (i=1;i<=n;i++){ size[i]=1; ft[i]=i; root[i]=i; e[i].push_back(i); } for (i=1;i<=m;i++) if (v[i]==0){ m1=get_ft(edge[i].x); m2=get_ft(edge[i].y); if (m1==m2) continue; if (e[m1].size()<e[m2].size()) get_together(m1,m2); else get_together(m2,m1); } for (i=q;i>0;i--){ if (s[i]=='F'){ m1=get_ft(g[i].x); cnt+=query(g[i].y,m1); tot++; } else if (s[i]=='U'){ m1=get_ft(g[i].x); del(g[i].x,m1); data[g[i].x]=g[i].y; ins(g[i].x,m1); } else if (s[i]=='E'){ m1=get_ft(g[i].x); m2=get_ft(g[i].y); if (num[i]==0) continue; v[num[i]]=0; if (m1==m2) continue; if (e[m1].size()<e[m2].size()) get_together(m1,m2); else get_together(m2,m1); } } double ans=(double)cnt/tot; printf("%.3lf\n",ans); return 0; }
--------------------------------------------华丽的分割线--------------------------------------------
暴力链表会吧,空间O(10^9)。。。。10分
朴素暴力会吧,时间O(n^2)。。。。30分
用Splay优化朴素暴力,离散化啥的,呵呵呵呵呵。。。。。。
P很好做啊,Splay维护就好了,关键是L咋办,怎么离散化,蛋疼啊。。。。
注意到问么其实对数列只进行了几次插入,也就是说很多子序列都是以公差为一递增的等差数列!
好有用的样子。这样我们只要保证每个连续的字序列至少开头在Splay树上,找到开头加一下就得到答案了!
分析一下插队的特点(a,b)我们只要把a,a+1,b都放到树里就行了
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define update(x) if (tip[x]!=0) push(x) const int Maxn=200005; int fa[Maxn],son[Maxn][2],rk[Maxn],tip[Maxn],a[Maxn],b[Maxn],c[Maxn]; int n,m,N,x,i,ta,tb,root,q[Maxn]; char s[Maxn]; void push(int x){ int lc=son[x][0], rc=son[x][1]; if (lc){ rk[lc]+=tip[x]; tip[lc]+=tip[x]; } if (rc){ rk[rc]+=tip[x]; tip[rc]+=tip[x]; } tip[x]=0; } void rotate(int x,int f,int K){ if (fa[f]){ if (son[fa[f]][0]==f) son[fa[f]][0]=x; else son[fa[f]][1]=x; } fa[x]=fa[f]; son[f][K]=son[x][K^1]; if (son[x][K^1]) fa[son[x][K^1]]=f; son[x][K^1]=f; fa[f]=x; } void Splay(int x,int y){ int f, gf; while (fa[x]!=y){ f=fa[x]; gf=fa[f]; update(gf); update(f); update(x); if (gf==y){ if (son[f][0]==x) rotate(x,f,0); if (son[f][1]==x) rotate(x,f,1); } else { if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0); if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1); if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1); if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0); } } update(x); if (y==0) root=x; } int build(int l,int r){ if (l>r) return 0; int mid=(l+r)>>1; rk[mid]=c[mid]; if (l==r) return l; son[mid][0]=build(l,mid-1); son[mid][1]=build(mid+1,r); fa[son[mid][0]]= fa[son[mid][1]]= mid; return mid; } void del(int x,int K){ Splay(x,0); if (son[x][0]) rk[son[x][0]]+=K, tip[son[x][0]]+=K; int pr=son[x][0], nx=son[x][1]; if (pr==0) { root=son[x][1]; fa[root]=0; return; } if (nx==0) { root=son[x][0]; fa[root]=0; return; } while (son[pr][1]){ update(pr); pr=son[pr][1]; } while (son[nx][0]){ update(nx); nx=son[nx][0]; } Splay(pr,0); Splay(nx,pr); son[nx][0]=0; } void ins(int x,int y,int K){ Splay(y,0); rk[x]=rk[y]-1; son[x][0]= son[x][1]= 0; while (y){ update(y); if (rk[y]>rk[x]){ if (son[y][0]==0) {fa[ son[y][0]=x ]=y; break;} else y=son[y][0]; } else { if (son[y][1]==0) {fa[ son[y][1]=x ]=y; break;} else y=son[y][1]; } } Splay(x,0); if (son[x][0]) rk[son[x][0]]+=K, tip[son[x][0]]+=K; } void work1(int x){ x=lower_bound(c,c+N+1,x)-c; Splay(x,0); printf("%d\n",rk[x]); } void work2(int x){ int y=root, z; while (y){ update(y); if (rk[y]==x) {printf("%d\n",c[y]);return;} if (rk[y]>x) y=son[y][0]; else z=y, y=son[y][1]; } printf("%d\n", c[z]+(x-rk[z]) ); } int main(){ freopen("queue.in","r",stdin); freopen("queue.out","w",stdout); scanf("%d",&n); c[N=1]=1; c[N=2]=1000000000; for (i=1;i<=n;i++){ scanf("%d%d",&a[i],&b[i]); c[++N]=a[i]; c[++N]=b[i]; c[++N]=a[i]+1; } scanf("%d\n",&m); for (i=1;i<=m;i++){ scanf("%c %d\n",&s[i],&q[i]); if (s[i]=='P') c[++N]=q[i]; } sort(c+1,c+N+1); N=unique(c+1,c+N+1)-c-1; root=build(1,N); for (i=1;i<=n;i++){ a[i]=lower_bound(c+1,c+N+1,a[i])-c; b[i]=lower_bound(c+1,c+N+1,b[i])-c; del(a[i],1), ins(a[i],b[i],-1); } for (i=1;i<=m;i++){ if (s[i]=='P') work1(q[i]); else work2(q[i]); } return 0; }
--------------------------------------------华丽的分割线--------------------------------------------
有了这次教训,看来数据结构还是要加强,这几天写写树剖和LCT吧
bless all~~~~