1.【bzoj 4552】[Tjoi2016&Heoi2016]排序
题意:给出一个1到n的全排列,现在对这个全排列序列进行m次局部排序。排序分为两种:(0,l,r)表示将区间[l,r]的数字升序排序;(1,l,r)表示将区间[l,r]的数字降序排序。最后询问第q位置上的数字。
分析:二分答案,将所有值小于等于当前值的数赋为0,其余赋为1。利用线段树可以通过统计区间内数字1的个数来使当前区间有序。进行m次局部排序后可以得到答案与当前值的大小关系,满足可二分性。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #define l(x) x<<1 5 #define r(x) x<<1|1 6 using namespace std; 7 const int N=1e5+10; 8 int n,m,now,L,R,pos,ans,sum; 9 int a[N],t[N*4],tag[N*4]; 10 struct node{int op,l,r;}b[N]; 11 int read() 12 { 13 int x=0,f=1;char c=getchar(); 14 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 15 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 16 return x*f; 17 } 18 void up(int x){t[x]=t[l(x)]+t[r(x)];} 19 void dn(int x,int l,int r) 20 { 21 int mid=(l+r)>>1; 22 tag[l(x)]=tag[x];t[l(x)]=tag[x]*(mid-l+1); 23 tag[r(x)]=tag[x];t[r(x)]=tag[x]*(r-mid); 24 tag[x]=-1; 25 } 26 void build(int x,int l,int r) 27 { 28 tag[x]=-1; 29 if(l==r){t[x]=(a[l]>now);return;} 30 int mid=(l+r)>>1; 31 build(l(x),l,mid); 32 build(r(x),mid+1,r); 33 up(x); 34 } 35 void change(int x,int l,int r,int p) 36 { 37 if(l!=r&&tag[x]!=-1)dn(x,l,r); 38 if(L<=l&&R>=r){tag[x]=p;t[x]=p*(r-l+1);return;} 39 int mid=(l+r)>>1; 40 if(L<=mid)change(l(x),l,mid,p); 41 if(R>mid)change(r(x),mid+1,r,p); 42 if(l!=r)up(x); 43 } 44 void ask(int x,int l,int r) 45 { 46 if(l!=r&&tag[x]!=-1)dn(x,l,r); 47 if(L<=l&&R>=r){sum+=t[x];return;} 48 int mid=(l+r)>>1; 49 if(L<=mid)ask(l(x),l,mid); 50 if(R>mid)ask(r(x),mid+1,r); 51 } 52 int main() 53 { 54 n=read();m=read(); 55 for(int i=1;i<=n;i++)a[i]=read(); 56 for(int i=1;i<=m;i++) 57 b[i].op=read(),b[i].l=read(),b[i].r=read(); 58 pos=read(); 59 int l=1,r=n; 60 while(l<=r) 61 { 62 now=(l+r)>>1;build(1,1,n); 63 for(int i=1;i<=m;i++) 64 { 65 L=b[i].l;R=b[i].r; 66 sum=0;ask(1,1,n); 67 if(!b[i].op)sum=b[i].r-b[i].l+1-sum; 68 L=b[i].l;R=b[i].l+sum-1; 69 if(L<=R)change(1,1,n,b[i].op); 70 L=b[i].l+sum;R=b[i].r; 71 if(L<=R)change(1,1,n,1-b[i].op); 72 } 73 L=R=pos;sum=0;ask(1,1,n); 74 if(sum)l=now+1; 75 else ans=now,r=now-1; 76 } 77 printf("%d",ans); 78 return 0; 79 }
2.【bzoj 2144】跳跳棋
题意:棋盘上有3颗棋子,分别在a,b,c位置,目标是通过最少的跳动把他们的位置移成x,y,z。跳动的规则即为任意选一颗棋子,对一颗中轴棋子跳动,跳动后两颗棋子距离不变。一次只允许跳过1颗棋子。首先判断是否可以完成任务;如果可以,输出最少需要的跳动次数。
分析:中间的棋子可以往两侧跳,但两侧的棋子只有一个能往中间跳(跟中间棋子距离较小的那一个)。那么所有的状态就能表示为一棵二叉树,在树上往下走即为中间向两边跳。问题转换为给定树上的两个结点,求其距离。在当前状态在树上往上走的过程中利用了辗转相除的思想。
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 const int inf=1e9; 6 int tmp,d1,d2,ans,a[4],b[4]; 7 struct node{int x[4];}t1,t2; 8 int read() 9 { 10 int x=0,f=1;char c=getchar(); 11 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 12 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 13 return x*f; 14 } 15 node calc(int *a,int k)//两边向中间跳k次,即在树上往上走k步 16 { 17 node ans; 18 int t1=a[2]-a[1],t2=a[3]-a[2]; 19 for(int i=1;i<=3;i++)ans.x[i]=a[i]; 20 if(t1==t2)return ans; 21 if(t1<t2)//左边往中间跳 22 { 23 int t=min(k,(t2-1)/t1); 24 k-=t;tmp+=t;//tmp记录深度,即实际步数 25 ans.x[2]+=t*t1;ans.x[1]+=t*t1; 26 } 27 else//右边往中间跳 28 { 29 int t=min(k,(t1-1)/t2); 30 k-=t;tmp+=t; 31 ans.x[2]-=t*t2;ans.x[3]-=t*t2; 32 } 33 if(k)return calc(ans.x,k);//辗转相除 34 return ans; 35 } 36 bool operator != (node a,node b) 37 { 38 for(int i=1;i<=3;i++) 39 if(a.x[i]!=b.x[i])return true; 40 return false; 41 } 42 int main() 43 { 44 for(int i=1;i<=3;i++)a[i]=read(); 45 for(int i=1;i<=3;i++)b[i]=read(); 46 sort(a+1,a+4);sort(b+1,b+4); 47 t1=calc(a,inf);d1=tmp;tmp=0; 48 t2=calc(b,inf);d2=tmp;tmp=0; 49 if(t1!=t2){printf("NO");return 0;} 50 if(d1>d2) 51 { 52 swap(d1,d2); 53 for(int i=1;i<=3;i++)swap(a[i],b[i]); 54 } 55 t2=calc(b,d2-d1); 56 for(int i=1;i<=3;i++)b[i]=t2.x[i];//调整到同一深度 57 int l=0,r=d1,mid; 58 while(l<=r) 59 { 60 mid=(l+r)>>1; 61 if(calc(a,mid)!=calc(b,mid))l=mid+1; 62 else r=mid-1; 63 } 64 printf("YES %d",d2-d1+2*l); 65 return 0; 66 }