【JOISC2018|2019】【20190622】minerals

题目

交互题

\(2n\)个物品,编号为\(1-2n\),存在唯一的两两配对关系,即有\(n\)种物品

有一个盒子,初始为空,盒子上会显示里面存在的物品种类数\(C\)

你每次操作可以将一个物品从盒子里拿出或者放入盒子

$n \le 43000 $,次数限制\(10^6\)

题解

  • 首先依次加入所有物品,考虑C变和不变可以将物品分成两个对应的集合AB

  • 在盒子里保留A的一半,依次改变B的状态,考虑C变和不变可以将B继续分成对应的两个集合

  • 交换AB,一直分治下去,复杂度大约是\(O(3.5N+2NlogN)\)

  • 然而这题最优秀的一点在于后面(图片来自官解):

    我们假设分治部分的次数复杂度为\(f(N) = tN \ logN\) ,设每次分治的两部分之比为p : 1 - p

    img

    (求导)求极值点:

    img

  • 那 p 就一反套路地取0.382好了

    #include "minerals.h"
    #include<bits/stdc++.h>
    #define K 0.38
    
    using namespace std;
    
    const int maxN=43010;
    int n,P[maxN],Q[maxN],vis[maxN<<1];
    int pl[maxN],pr[maxN],ql[maxN],qr[maxN];
    
    bool query(int x){
    	static int now,lst,re;
    	vis[x]^=1;now=Query(x);
    	re=(now!=lst);lst=now;
    	return re;
    }
    
    void Swap(int l1,int r1){
    	static int tmp[maxN];
    	for(int i=1;i<=l1;++i)tmp[i]=pl[i];
    	for(int i=1;i<=r1;++i)pl[i]=pr[i];
    	for(int i=1;i<=l1;++i)pr[i]=tmp[i];
    }//直接swap两个指针的话似乎会把指向的数组全部交换
    
    void solve(int*p,int*q,int len){
    	if(len==1){
    		Answer(p[1],q[1]);
    		return;
    	}
    	int l1,r1,l2,r2;
    	l1=r1=l2=r2=0;
    	for(int i=1;i<=len;++i){
    		if(vis[p[i]])pl[++l1]=p[i];
    		else pr[++r1]=p[i];
    	}
    	if(l1>r1)Swap(l1,r1),swap(l1,r1);
    	int base=max(1,(int)(K*len));
    	while(l1<base)query(pr[r1]),pl[++l1]=pr[r1--];
    	while(l1>base)query(pl[l1]),pr[++r1]=pl[l1--];
    	if(vis[pl[1]])Swap(l1,r1),swap(l1,r1);
    	for(int i=1;i<=len;++i)if(query(q[i])){
    		ql[++l2]=q[i];
    		if(l2==l1){for(++i;i<=len;++i)qr[++r2]=q[i];}
    	} else{
    		qr[++r2]=q[i];
    		if(r2==r1){for(++i;i<=len;++i)ql[++l2]=q[i];}
    	}
    	for(int i=1;i<=l1;++i)p[i]=pl[i];
    	for(int i=1;i<=l2;++i)q[i]=ql[i];
    	for(int i=1;i<=r1;++i)p[i+l1]=pr[i];
    	for(int i=1;i<=r2;++i)q[i+l2]=qr[i];	
    	solve(q,p,l1);
    	solve(q+l1,p+l1,r1);
    }
    
    void Solve(int N) {
    	n=N;
    	int cnt1=0,cnt2=0;
    	for(int i=1;i<=2*n;++i){
    		if(query(i))P[++cnt1]=i;
    		else Q[++cnt2]=i;
    	}	
    	solve(P,Q,n);
    }
    //一道非常有意思的交互题
    //20190622
    
posted @ 2019-06-23 19:07  大米饼  阅读(296)  评论(0编辑  收藏  举报