• 「WC2019」 I 君的商店 题解


    神题,目前学 OI 以来遇到过的最强的题。

    简要题意:一个值域为 \([0,1]\) 的长度为 \(n\) 的序列 \(a\) 被隐藏了起来,初始时我们知道 \(a\) 中至少有一个 \(1\)\(1\) 的奇偶性,每次可以询问两个集合 \(S,T\)交互器会返回是 \(S\ge T\) 还是 \(T\ge S\),一次询问的代价是 \(T\) 的大小与 \(S\) 的大小之和,你需要使得总代价尽可能的小,大概的范围是 \(5n+6\log n\)

    Solution

    算法 1

    考虑每个人大力两两比较一次,那么每个人也就比较 \(n-1\) 次,然后可以把比较胜利的次数排个序,为 \(1\) 元素的胜利次数一定大于为 \(0\) 元素的胜利次数。
    那么一定存在一个分界点,分界点左右分别是 \(0,1\),不难发现此时最大值一定是排序出的最大值,那么其答案一定为 \(1\)
    接下来将相邻的两个元素绑在一起作为 \(T\) 与这个 \(1\) 作为 \(S\) 问一下,那么问出来的一定是一段 \(T\) 大一段 \(S\) 大,但是 \(0,1\) 相邻的情况不好搞,而且 \(S,T\) 也会相邻,我们无法确定相邻的 \(S,T\) 哪个对应着 \(0,1\) 相邻,所以需要依赖奇偶性判断一下。
    此时相邻问其实可以使用二分优化来确定分界点。
    时间复杂度 \(O(n^2)\),询问费用 \(n\times (n-1)+3\log n\),期望得分 \(40\) 分。
    大力两两比较有必要吗?直接快排不好吗?
    时间复杂度 \(O(n\log n)\),询问费用 \(n\log n+3\log n\),期望得分 \(52\) 分。

    算法 2

    注意到本题只有 \(0,1\) 两种权值,不如思考,排序究竟有没有用。
    尝试找出一个最大值 \(q\),显然这个值可以在 \(2n\) 的代价内找出,接下来利用这个元素来推断。
    如果当前只剩下一个元素没有被推出,那么可以直接依赖奇偶性推断,考虑其他元素,任选两个元素 \(x,y\),假设 \(x\ge y\),接下来询问 \(\{x,y\}\)\(\{q\}\)

    • 如果 \(\{x,y\}\ge \{q\}\),那么 \(x,y\) 至少一个为 \(1\),又有 \(x\ge y\),那么 \(x=1\)
    • 否则,那么 \(x,y\) 至少一个为 \(0\),又有 \(x\ge y\),那么 \(y=0\)

    不难发现每次询问至少会确定一个,把确定的换掉就可以了,时间复杂度 \(O(n)\),询问费用 \(2n+5n=7n\),期望得分 \(69\) 分。

    算法 3

    不难发现确定元素的步骤似乎无法优化,那么最大值可不可以删去呢?答案是可以的。
    取一个元素作为基准值,接下来考虑如果其是 \(0\) 如何处置。
    任取元素 \(a,b,c\),同样假设 \(b\ge c\),询问 \(\{b,c\}\)\(\{a\}\)

    • 如果 \(\{b,c\}\le \{a\}\),那么 \(b,c\) 必然不全是 \(1\),那么 \(c=0\),换掉 \(c\)
    • 否则,如果 \(a\)\(1\),则 \(b\) 一定是 \(1\),也就意味着 \(b\ge a\),用 \(b\) 替换 \(a\),并换一个新 \(b\) 上来。

    结束上述过程之后,我们也就可以确定若干个元素,确定一条 \(x_1\ge x_2\ge x_3\ge \ldots \ge x_k\) 的链,和一个未知元素 \(p\)
    不难发现 \(x_k\)\(p\) 中必有一个为 \(1\),如果 \(p\) 位置是 \(1\),那么可以把 \(p\) 接在 \(x_k\) 后面。
    那么链上的节点的分界点可以使用算法 1 确定,再利用奇偶性确定 \(p\) 与分界点元素。
    时间复杂度 \(O(n)\),询问费用 \(O(5n+3\log n)\),期望得分 \(100\) 分。

    Code
    void chkmax(int &x,int y) {if(x<y) x=y;}
    void chkmin(int &x,int y) {if(x>y) x=y;}
    
    const int N=1e5;
    int id[N+10],tot;
    int query_(int x,int y) {
    	int S[1]={x},T[1]={y};
    	return query(T,1,S,1);
    }
    int query__(int a,int x,int y) {
    	int S[1]={a},T[2]={x,y};
    	return query(T,2,S,1);
    }
    void find_price(int myd,int n,int K,int ans[]) {
    	if(n==1) {ans[0]=1;return;}
    	if(n==2) {
    		if(K%2==0) ans[0]=1,ans[1]=1;
    		else {
    			bool res=query_(0,1);
    			ans[res]=1,ans[res^1]=0;
    		}
    		return;
    	}
    	if(myd==3) {
    		for(int i=0;i<n;i++) id[i]=i;
    		if(!query_(0,n-1)) reverse(id,id+n);
    		int L=0,R=n;
    		while(L+1<R) {
    			int mid=(L+R)>>1;
    			if(mid<n-1&&!query__(id[0],id[mid],id[mid+1])) L=mid;
    			else R=mid;
    		}
    		int t=(L+1-K)&1;
    		ans[id[R]]=t;
    		for(int i=0;i<=L;i++) ans[id[i]]=1;
    		return;
    	}
    	tot=0;
    	int a=0,x=1,y=2,p1;
    	id[tot++]=p1;
    	for(int i=3;i<=n;i++) {
    		if(query_(x,y)) swap(x,y);
    		if(query__(a,x,y)) x=i;
    		else id[tot++]=a=y,y=i;
    	}
    	reverse(id,id+tot);
    	x=x<n?x:y;
    	int mx=query_(id[0],x)?id[0]:x;
    	int L=0,R=tot;
    	while(L+1<R) {
    		int mid=(L+R)>>1;
    		if(mid<tot-1&&!query__(mx,id[mid],id[mid+1])) L=mid;
    		else R=mid;
    	}
    	int t=(L+1-K)&1;
    	if(t) {
    		if(query_(id[R],x)) ans[id[R]]=1;
    		else ans[x]=1;
    	}
    	else if(!query__(mx,id[R],x)) ans[id[R]]=ans[x]=1;
    	for(int i=0;i<=L;i++) ans[id[i]]=1;
    	return;
    }
    
  • 相关阅读:
    高手 读书笔记-1.精英社会的神话
    企业IT架构转型之道 读书笔记-1.阿里巴巴集团中台战略引发的思考
    Redis学习-内存优化
    Redis学习笔记--常用命令
    Go语言备忘录(3):net/http包的使用模式和源码解析
    Go语言备忘录(2):反射的原理与使用详解
    Go语言备忘录(1):基本数据结构
    Go的接口总结
    Go的类型断言解析
    StrangeIoc框架学习
  • 原文地址:https://www.cnblogs.com/cnyzz/p/15543412.html
Copyright © 2020-2023  润新知