您的位置:首页 > 其它

省选专练[SCOI2014]方伯伯的OJ

2018-02-26 22:14 274 查看
大大大大大!!!!!!数据结构题。
太大了:
法一:线段树动态开点:注意n有1e8于是考虑m影响的。
法二:两个SPLAY:目的是在n有1e8时一棵维护名次,一棵维护编号。目的是解决n
原理:修改k:则剖成(1,k-1)(k,k)(k+1,n);
最多复杂度:logn*m;
法三::一个SPLAY,另外一个用map就可以了。用一个in记录是否到了前端与后缀。#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
inline void read(int &x){//fast read
int f=1;
x=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
x*=f;
}
int n,m,d;//d is the max num that the tree could expand using by the SET
struct SET{//a set to make sure the line is priority
#define N (int) 2e5
int a[N*3];
int id
;//idx just record the left sum and that is priority
int n;
int insert(int idx){//维持单调的本质原因是为了保证在查询编号时可以得到在哪一块
n++;
id
=idx;
int i=n+d;//we have two sum should be recorded;
a[i]=1;
while(i>>=1){
a[i]++;
}
return n;
}
void del(int i){// del a point and expand 1 to 3 points
i+=d;
a[i]=0;
while(i>>=1){
a[i]--;
}
}
int rank(int i){//give you a sum and que the rank;
i+=d;
int ans=a[i];
while(i){
if(i%2==1){
ans+=a[i-1];
}
i/=2;
}
return ans;
}
int get_by_rank(int x){//give you the rank ans que the sum;
int i=1;
while(i<=d){
if(x<=a[i*2]){
i*=2;
}
else{
x-=a[i*2];
i=i*2+1;
}
}
return id[i-d];
}
}pre,suf;//pre尚未开封的左端(名次小)suf尚未开封的右端(名次大)
struct SPlAY{
#define cl(x) c[x][0]
#define cr(x) c[x][1]
int fa
;
int c
[2];
int siz
;
int v
;
int rt;
int n;
int rank(int x){
int ans=x;
int i=rt;
while(i){
if(v[i]>x){
i=cl(i);
}
else{
ans-=siz[cl(i)]+1;
i=cr(i);
}
}
return ans;
}
void sc(int y,int x,bool flag){
fa[x]=y;
c[y][flag]=x;
}
bool get(int x){
return x==c[fa[x]][1];
}
void pushup(int x){
siz[x]=siz[cl(x)]+siz[cr(x)]+1;
}
void rotate(int x){
int y=fa[x];
bool flag=get(x);
if(y==rt){
fa[rt=x]=0;
}
else{
sc(fa[y],x,get(y));
}
sc(y,c[x][!flag],flag);
sc(x,y,!flag);
pushup(y);
}
void splay(int x,int to=0/*depends on the root*/){
int y;
while(y=fa[x],y!=to){
if(fa[y]==to){
rotate(x);
break;
}
else{
rotate(get(x)==get(y)?y:x);
rotate(x);
}
}
pushup(x);
}
void insert(int x){//insert the sum to the SPLAY tree
n++;
v
=x;
siz
=1;
if(!rt){
rt=n;
return;
}
int i=rt;
while(1){
bool d=v[i]<x;
if(c[i][d])
i=c[i][d];
else{
sc(i,n,d);
splay(n);
return;
}
}
}
map<int,int> dy;
int DY(int x){//hash the changing name of the num
if(!dy.count(x)){
return x;
}
return dy[x];
}
int get_by_rank(int x){//got the sum get the rank值找编号
if(!rt){
return DY(x);
}
int i=rt;
int i0;
int rank=1;
while(i){
i0=i;
if(x>v[i]-(siz[cl(i)]+rank)){
rank+=siz[cl(i)]+1;
i=c[i][1];
}
else{
i=c[i][0];
}
}
splay(i0);
return DY(x+rank-1);
}
}out;
const int PRE=1;//PRE means at the head of the queue
const int SUF=2;//SUF means at the tail of the queue
struct point{
int in,id;//in means if that has been in the queue already and the location that is
};
map<int,point> belong;//the location of the i;
point &B(int x){//这个函数的目的实在映射中寻找这个编号的位置.打传递是因为要通过这个改变belong所hash的值
if(!belong.count(x))
belong[x]=(point){0,x};//如果没有hash过那么必然这时还没有过挪动到队首或队尾
return belong[x];
}
int get_rank(int x){//已知编号求名次
if(B(x).in==1){
return pre.a[1]+1-pre.rank(B(x).id);//a 数组在这里表示最左端名次(最大),然后加一是因为计算自己。
}
if(B(x).in==2){
return n-suf.a[1]+suf.rank(B(x).id);
}
return pre.a[1]+out.rank(B(x).id);//如果已经加入splay内,则先加上右端所有的值然后再加上splay中的rank
}
//引理:pre中的最大名次<out中的最小名次<=out中的最大名次<suf中的最小名次。
int get_by_rank(int x){//已知名次求编号。
if(x<=pre.a[1]){//引理1
return pre.get_by_rank(pre.a[1]-x+1);//在pre中的名次是总名次-实际名次+1,因为pre是倒过来的
}
if(x>n-suf.a[1]){//引理3
return suf.get_by_rank(x-(n-suf.a[1]));//减去suf之前的所有名次,则是在suf中的实际名次。
}//引理2
return out.get_by_rank(x-pre.a[1]);//减去pre中的所有名次则是在out中的名次。
}
int main(){
read(n);
read(m);
// cout<<n<<" "<<m<<endl;
for(d=1;d<m;d*=2);// get the max sum may be
d--;
int lastans=0;//must online
for(int i=1;i<=m;i++){
// cout<<"number "<<i<<endl;
int flag;
read(flag);
int ans;
if(flag==4){
int k;
read(k);
k-=lastans;
ans=get_by_rank(k);//已知名次反向求编号。
}
else{
int k;
read(k);
k-=lastans;
ans=get_rank(k);//这三中操作都是要求用编号求名次。
if(flag==1){
int to;
read(to);
to-=lastans;
B(to)=B(k);
if(B(k).in){//出现 in 则必然是改变块,否则是原块
(B(k).in==PRE?pre:suf).id[B(k).id]=to;//change the sum to the loc should be and hash the change sum
}
else{
out.dy[B(k).id]=to;//if not hash the change sum;
}
}
else{//必然会挪动到队首或队尾
if(B(k).in==0){//如果没有挪动过,那么在SPLAY插入这个下标。
out.insert(B(k).id);
}
else
(B(k).in==PRE?pre:suf).del(B(k).id);//因为会被挪动过,所以删去原本的点。
if(flag==2){
B(k)=(point){PRE,pre.insert(k)};//go ahead
}
else{
B(k)=(point){SUF,suf.insert(k)};//turn back
}
}
}
printf("%d\n",ans);
lastans=ans;//must online
}
return 0;//or wrong answer
}注解很丰富。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: