列队[noip2017]
其实从我开始学splay后,我就想过开n棵spaly硬钢这道题
后来我又想到动态开点
可惜我不会
终于今天,嗯,我A了它,真好
#include<bits/stdc++.h> #define re return #define LL long long #define inc(i,l,r) for(LL i=l;i<=r;++i) using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=3000000; LL n,m,tot,cnt,Q; int fa[maxn],son[maxn][2]; LL size[maxn],ll[maxn],rr[maxn]; /*此题区间左闭右开*/ struct Splay { int rt; //建新点 inline int New_point(LL l,LL r) { ++cnt; fa[cnt]=son[cnt][0]=son[cnt][1]=0; ll[cnt]=l;rr[cnt]=r;size[cnt]=r-l; re cnt; } //saply常规操作 inline int chk(int x){re son[fa[x]][1]==x;} inline void pushup(int x) { size[x]=size[son[x][0]]+size[son[x][1]]+rr[x]-ll[x]; } inline void rotate(int x) { int y=fa[x],z=fa[y],f=chk(x),w=son[x][f^1]; son[z][chk(y)]=x;fa[x]=z; son[x][f^1]=y;fa[y]=x; son[y][f]=w;fa[w]=y; pushup(y); pushup(x); } inline void splay(int x,int goal=0) { while(fa[x]!=goal) { int y=fa[x],z=fa[y]; if(z!=goal) chk(x)==chk(y)?rotate(y):rotate(x); rotate(x); } if(!goal)rt=x; } //动态开点平衡树 (将n多操作合二为一) //你从未见过的船新操作,动态开点 //将前k个点放入原根,后面新开节点 inline LL Split_point(int u,LL k) { k+=ll[u]; int y=New_point(k,rr[u]); rr[u]=k; if(!son[u][1]) fa[son[u][1]=y]=u; else { int t=son[u][1]; while(son[t][0])t=son[t][0]; fa[son[t][0]=y]=t; while(t!=u)pushup(t),t=fa[t]; } splay(y); re y; } //找出第K大点并弹出 LL Pop_kth(LL k) { int u=rt; while(2333) { int y=son[u][0]; if(size[y]>=k)u=y; else { k-=size[y]; if(rr[u]-ll[u]<k)k=k-(rr[u]-ll[u]),u=son[u][1]; else { if(k!=rr[u]-ll[u])Split_point(u,k); if(k!=1)u=Split_point(u,k-1); break; } } } splay(u); fa[son[u][0]]=fa[son[u][1]]=0; if(!son[u][0]) { rt=son[u][1]; } else { int t=son[u][0]; while(son[t][1])t=son[t][1]; splay(t); rt=t; fa[son[t][1]=son[u][1]]=t; pushup(t); } re ll[u]; } //找到最后,并放进新数 inline void Push_back(LL x) { int p=New_point(x,x+1); if(!rt) rt=p; else { int u=rt; while(son[u][1]) u=son[u][1]; splay(u); son[fa[p]=u][1]=p; pushup(u); } } }s[maxn]; int main() { //freopen("in.txt","r",stdin); freopen("testdata.in","r",stdin); LL x,y,p; rd(n),rd(m),rd(Q); //建树 inc(i,1,n) s[i].rt=s[i].New_point((i-1)*m+1,i*m); s[0].rt=s[0].New_point(m,m+1); inc(i,2,n) s[0].Push_back(i*m); inc(i,1,Q) { rd(x),rd(y); s[x].Push_back(s[0].Pop_kth(x)); printf("%lld\n",p=s[x].Pop_kth(y)); s[0].Push_back(p); } re 0; }
当然,动态开点权值线段树什么的最适合这道题了
/* 魔改程序伤不起 LL落下两行泪 */ #include<bits/stdc++.h> #define re return #define ll long long #define inc(i,l,r) for(ll i=l;i<=r;++i) using namespace std; template<typename T>inline void rd(T&x) { char c;bool f=0; while((c=getchar())<'0'||c>'9')if(c=='-')f=1; x=c^48; while((c=getchar())>='0'&&c<='9')x=x*10+(c^48); if(f)x=-x; } const int maxn=7000000; ll n,m,tot,cnt,Q,flag,ans; ll x,y; ll use[maxn],ls[maxn],rs[maxn],num[maxn]; ll sum[300005],T[300005]; inline void query(ll &rt,ll l,ll r,ll k) { if(!rt)rt=++cnt; //没有访问过 开点 ++use[rt]; //因为下面用的是use[ls[rt]],没有影响 if(l==r) { if(num[rt])ans=num[rt]; else if(!flag)ans=l*m; else ans=(x-1)*m+l; re ; } ll mid=(l+r)>>1; ll cur=mid-l+1-use[ls[rt]]; //标记真实存在的个数 if(cur>=k)query(ls[rt],l,mid,k); else query(rs[rt],mid+1,r,k-cur); re ; } inline void insert(ll &rt,ll l,ll r,ll pos) { if(!rt)rt=++cnt; //没有访问过 开点 if(l==r) { num[rt]=ans; re; } ll mid=(l+r)>>1; if(pos<=mid)insert(ls[rt],l,mid,pos); else insert(rs[rt],mid+1,r,pos); } int main() { //freopen("in.txt","r",stdin); freopen("testdata.in","r",stdin); ll p,t; rd(n),rd(m),rd(Q); inc(i,1,Q) { rd(x),rd(y); if(y==m) { flag=0; query(T[n+1],1,n+Q,x); printf("%lld\n",ans); ++sum[n+1];//现在放入位置+n insert(T[n+1],1,n+Q,sum[n+1]+n);
//预留好足够大的位置 //加入末尾 } else { flag=x; query(T[x],1,m+Q,y); printf("%lld\n",ans); ++sum[n+1]; insert(T[n+1],1,n+Q,sum[n+1]+n); flag=0; query(T[n+1],1,n+Q,x); ++sum[x]; insert(T[x],1,m+Q,sum[x]+m-1); //此处之所以,是因为每一行的m列不被包括在那一行中
//将每一行中的m抹去
} } re 0; }
我太蒟蒻了,还没理解树状数组的做法,待我有时