1 条题解

  • 0
    @ 2025-8-24 22:08:16

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar BFqwq
    青く广がった あの空のように、昙りなく笑えたら あなたに会いたい。

    搬运于2025-08-24 22:08:16,当前版本为作者最后更新于2020-11-24 18:56:51,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    P5208 【[WC2019]I 君的商店】

    第一次过交互题,可能也是我退役之前过的唯一一道交互题了。

    为了方便叙述,令物品 ii 的价格为 viv_i

    sub 1,2,4,5

    首先,我们可以先得出两个显然的结论:

    1. 如果 vbvcv_b\le v_cvavb+vcv_a\ge v_b+v_c,则我们可以直接推出 vb=0v_b=0,对于任意 a,b,ca,b,c 成立(否则 vb=vc=1,vb+vc=2>1vav_b=v_c=1,v_b+v_c=2>1\ge v_a,矛盾)。

    2. 如果 vbvcv_b\le v_cvavb+vcv_a\le v_b+v_c,则我们可以直接推出 vavcv_a\le v_c,对于任意 a,b,ca,b,c 成立(否则 va=1,vb=vc=0,vb+vc=0<1=vav_a=1,v_b=v_c=0,v_b+v_c=0<1=v_a,矛盾)。特别的,如果已知 va=1v_a=1,则可以据此确定 vc=1v_c=1

    然后我们发现,如果我们能够确定一个数 vx=1v_x=1,那么接下来我们每消耗 55 点体力就可以确定一个数的值。

    而确定一个 11 这种事情显然很简单。我们可以直接进行 n1n-1 次比较,每次根据返回的数保留其中较大的那一个,这样我们就可以比出一个最大值(设为 tt)。

    而由于这个物品的价格大于等于所有其他物品的价格,且题目保证一定存在 11,所以我们可以确定 vtv_t 一定为 11

    然后我们再根据上述的两个结论,每次任取两个数 a,ba,b,先比较出其大小(不妨设 vavbv_a\le v_b),然后再加起来和 vtv_t 比较。

    如果 vtva+vbv_t\ge v_a+v_b,则根据结论 11 直接推出 va=0v_a=0,否则根据结论 22 直接推出 vb=1v_b=1

    最后我们会发现只剩一个数没法比较。但由于题目给定了奇偶性,所以我们可以根据目前的 11 的个数和奇偶性判断出最后一个数的 vv 值。

    在找 tt 的阶段我们消耗的体力为 2n2n,在确定所有数的阶段我们消耗的体力为 5n5n,共 7n7n,满足条件。

    但我们发现如果使用这种做法,想要再减少 2n2n 的消耗是非常困难的。不过没有关系,我们可以先解决 sub3 的问题。

    sub3

    在这个子任务中,我们看到了一个很好的性质:价值是单调递增(递减)的。我们可以很显然的想到使用二分。

    首先,我们可以通过判断 v0v_0vn1v_{n-1} 的相对大小来确定价值单调递增还是单调递减。不妨设序列单调递减。

    我们依然考虑采用前面的方法,将两个数和 11 作比较。

    由于序列一定存在一个 11,所以 v0=1v_0=1

    我们设 mid=(l+r)÷2mid=(l+r)\div 2,显然 vmiddmid+1v_{mid}\ge d_{mid+1}。我们将两者之和与 v0v_0 比较,那么我么一定可以得到 vmid=1v_{mid}=1vmid+1=0v_{mid+1}=0

    二分处那一个 0011 的边界。注意无论如何,中间必然有一个数时我们无法确定的,所以只剩一个数的时候我们要用奇偶性去判定。

    应该说思路非常简单,但代码实现却有一点难度,稍有不慎就会写错。

    我的写法是设 ll 为我们目前所能确定的最后一个 11r+2r+2 为我们目前所能确定的最后一个 00

    if(n<=2||sub==3){
    	for(int i=0;i<n;i++){
    		o[i]=i;
    	}
    	if(leq(0,n-1)){
    		reverse(o,o+n);
    	}
    	int l=0,r=n-1;
    	while(l<r){
    		int mid=l+r+1>>1;
    		if(mid<n-1&&leq(o[0],o[mid],o[mid+1])){
    			l=mid;
    		}
    		else{
    			r=mid-1;
    		}
    	}
    	ans[o[l+1]]=(l+1^k)&1;
    	for(int i=0;i<=l;i++){
    			ans[o[i]]=1;
    	}
    	return;
    }
    

    sub6

    受到 sub3 的做法的启发,我们可以考虑构造一个单调递增的序列,然后用这个在这个序列上进行二分。恰好数据范围多给了 100100 点体力,足够我们进行二分。

    每次,我们选取 a,b,ca,b,c 三个数进行比较,其中 aa 是单个的数,要和 b,cb,c两者的价值之和比较。

    每次先比较 b,cb,c 的大小。不妨设 vbvcv_b\le v_c

    如果 vavb+vcv_a\ge v_b+v_c,则直接推出 vb=0v_b=0,换一个 vbv_b 继续比。

    否则我们可以推出 vavcv_a\le v_c。接着,我们把 aa 放到一个数组中,然后用 cc 来作新的 aa,换一个 cc 继续比。

    举个例子,v2v3v_2\le v_3v1v2+v3v_1\le v_2+v_3,则我们得到 v1v3v_1\le v_3。我们将 11 放到数新组中,这时数组中只有一个元素 11,然后 a=3a=3,即 33 成为单个的数。

    然后 v2v4v_2\le v_4v3v2+v4v_3\ge v_2+v_4,则 v2=0v_2=0

    然后 v4v5v_4\le v_5v3v4+v5v_3\le v_4+v_5,则 v3v5v_3\le v_5,把 v3v_3 放入数组中,然后 a=5a=5,即 55 成为单个的数。

    接着我们发现,数组中的上一个数正好是 11,这时被 33 比下去的数。

    然后接下来如果有数进来,那一定是 55,然后 33 刚好又是被 55 比下去的。

    于是我们就发现这个数组是按照价值单调递增的。

    最后我们会剩下两个数,其中一个是 aa

    由于此时的 aa 已经大于等于数组中的所有数,而除了剩下的两个数之外的数要么在数组中,要么是 00,所以显然可以知道,所有数中的最大者一定在剩下的两个数中。

    然后我们比一次,就能比出一个较大者(设为 tt),vt=1v_t=1。另一个数设为 ss

    然后我们仿照 sub3 二分,直到数组中只剩一个数(设为 qq)未被确定。

    然后我们就只剩 s,qs,q 两者未被确定了。我们还是按比大小,然后价值求和与 vtv_t 比的方法确定其中的一个,然后依据奇偶性确定另一个即可。

    好像思路还挺简单的

    贴个代码,调了半天的(

    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+6;
    int tmp[2][2];
    int o[maxn],cnt;
    int query(int *S,int nS,int *T,int nT);
    bool leq(int a,int b){
    	tmp[0][0]=a;tmp[1][0]=b;
    	return query(tmp[0],1,tmp[1],1);
    }//return a<b
    bool leq(int a,int b,int c){
    	tmp[0][0]=a;tmp[1][0]=b;tmp[1][1]=c;
    	return query(tmp[0],1,tmp[1],2);
    }//return a<b+c
    void find_price(int sub,int n,int k,int ans[]){
    	cnt=0;
    	if(n<=2||sub==3){
    		for(int i=0;i<n;i++){
    			o[i]=i;
    		}
    		if(leq(0,n-1)){
    			reverse(o,o+n);
    		}
    		int l=0,r=n-1;
    		while(l<r){
    			int mid=l+r+1>>1;
    			if(mid<n-1&&leq(o[0],o[mid],o[mid+1])){
    				l=mid;
    			}
    			else{
    				r=mid-1;
    			}
    		}
    		ans[o[l+1]]=(l+1^k)&1;
    		for(int i=0;i<=l;i++){
    			ans[o[i]]=1;
    		}
    		return;
    	}
    	int a,b,c;
    	a=0,c=1;
    	for(int i=2;i<n;i++){
    		b=i;
    		if(leq(c,b)){
    			swap(b,c);
    		}
    		if(leq(a,b,c)){
    			o[cnt++]=a;
    			a=c;c=b;
    		}
    		else{
    			ans[b]=0;
    		}
    	}
    	if(leq(c,a)){
    		swap(a,c);
    	}
    	ans[c]=1;
    	if(!cnt){
    		ans[a]=k^1;
    		return;
    	}
    	o[cnt++]=c;
    	int l=0,r=cnt-1;
    	reverse(o,o+cnt);
    	while(l<r){
    		int mid=l+r+1>>1;
    		if(mid<cnt-1&&leq(o[0],o[mid],o[mid+1])){
    			l=mid;
    		}
    		else{
    			r=mid-1;
    		}
    	}
    	for(int i=0;i<=l;i++){
    		ans[o[i]]=1;
    	}//o[l+1] 和 a 未知
    	c=o[l+1];
    	if(leq(c,a)){
    		swap(a,c);
    	}
    	if(leq(o[0],c,a)){
    		ans[c]=1;
    		l++;
    	}
    	else{
    		ans[a]=0;
    		a=c;
    	}
    	ans[a]=(l+1^k)&1;
    	return;
    }
    
    • 1

    信息

    ID
    4181
    时间
    2000ms
    内存
    500MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者