Codeforces 1290D - Coffee Varieties(分块暴力+完全图的链覆盖)
Easy version:Codeforces 题面传送门 & 洛谷题面传送门
Hard version:Codeforces 题面传送门 & 洛谷题面传送门
发现自己交互题烂得跟 sh*t 一样……于是不管三七二十一先来两道再说(
首先考虑最 trivial 的情况,也就是 \(k=1\) 和 \(k=n\) 两种情况,对于 \(k=1\) 你就 \(\mathcal O(n^2)\) 地检查一遍所有的 pair,具体来说我们枚举所有 \(i,j(i<j)\),然后依次询问 \(i,j\),如果询问到 \(j\) 时发现队列中存在 \(i\),就说明在 \(j\) 前面有存在与 \(a_j\) 相同的值,此时 \(j\) 不会对答案产生贡献,因此我们考虑开一个数组 \(is_i\) 表示 \(i\) 是否对答案产生贡献,每访问到这样的 \(j\) 就将 \(is_j\) 设为 \(0\) 即可。最后统计 \(\sum\limits_{i=1}^nis_i\) 即可,总询问次数 \(\dbinom{n}{2}·2=n(n-1)\)
\(k=n\) 的情况更容易,直接扫一遍,如果插入一个 \(i\) 时发现 \(i\) 已经存在,就将 \(is_i\) 设为 \(0\) 即可,总查询次数 \(n\)。
现在继续思考更一般的情形,也就是 \(k\ne 1\) 且 \(k\ne n\) 的情形,我们考虑将两个暴力结合一下,也就是人们常说的分块暴力,我们考虑将每 \(\dfrac{k}{2}\) 个元素形成一块,询问时对每两个块 \(i,j(i<j)\) 跑一遍上面 \(k=n\) 的暴力然后清空队列,这样对于每一对满足 \(x<y\) 且 \(a_x=a_y\) 的 \((x,y)\),如果 \(x,y\) 在同一块中那只要扫到 \(x\) 所在的块就会统计到这个二元组,否则在遍历 \(x\) 所在的块与 \(y\) 所在的块时会遍历到该二元组,这也就证明了上述算法的正确性。
算下询问次数,总共 \(\dfrac{2n}{k}\) 个块,遍历次数 \(\dbinom{2n/k}{2}=\mathcal O(\dfrac{2n^2}{k^2})\),而每次遍历要检验 \(k\) 个元素,因此总操作次数 \(\dfrac{2n^2}{k}\),可以通过 easy version。
const int MAXN=1<<10;
int n,k,is[MAXN+5];
bool query(int x){
printf("? %d\n",x);fflush(stdout);
char c;cin>>c;return (c=='Y');
}
int main(){
scanf("%d%d",&n,&k);int siz=max(k>>1,1);
for(int i=1;i<=n;i++) is[i]=1;int cnt=n/siz;
for(int i=1;i<=cnt;i++) for(int j=i+1;j<=cnt;j++){
for(int l=(i-1)*siz+1;l<=i*siz;l++) is[l]&=!query(l);
for(int l=(j-1)*siz+1;l<=j*siz;l++) is[l]&=!query(l);
printf("R\n");fflush(stdout);
} int sum=0;for(int i=1;i<=n;i++) sum+=is[i];
printf("! %d\n",sum);fflush(stdout);
return 0;
}
接下来考虑 hard version,不难发现上述算法的瓶颈在于,每次遍历两块时都要重新清空队列,效率太低。考虑优化。我们考虑将所有待检验的二元组 \((i,j)\) 看作一个边,那么显然会连成一个 \(\dfrac{2n}{k}\) 个点的完全图(注:这里可能有人会有疑问,在之前的算法中我们不是强制钦定 \(i\) 必须小于 \(j\) 吗?那这里不应该也有 \(i<j\) 吗?为什么会得到一张完全图呢?事实上我们并不一定非得要 \(i<j\),如果我们采取这样一个思想:维护一个集合表示目前没有值与其重复的元素,对于某个 \(a_i\),如果我们发现在该集合中存在某个元素,满足值与其相同,就将 \(a_i\) 从集合中删除,如此进行下去直到集合中无法再删除元素。此时 \(is_i\) 的定义要变为 \(a_i\) 是否还在集合中。不难发现这两个思路是等价的,而后者并不需要用到 \(i<j\) 这个性质),我们的目标即是覆盖这些边。不难发现对于完全图中的一条长度为 \(l\) 的链 \(a_1\to a_2\to a_3\to\cdots\to a_l\),覆盖它们所需的最小代价为 \(l·\dfrac{k}{2}\),具体步骤就是对于这条链上所有点一一询问即可。而显然我们不会重复覆盖相同的边,因此对于某个链覆盖的方案,覆盖整张图所需的最少代价就是 \(\dfrac{k}{2}·(\dbinom{2n/k}{2}+\text{使用链的个数})\)。而对于一个 \(n\) 个点(\(n\) 是偶数)的完全图而言有一个性质,就是覆盖它的边集所用的最少的链的个数是 \(\dfrac{n}{2}\),具体构造是我们枚举所有 \(s\in[1,\dfrac{n}{2}]\),然后 \(s\to s+1\to s-1\to s+2\to s-2\) 这样之字形反复横跳,如果超过 \(n\) 就回到 \(1\),如果小于 \(1\) 就回到 \(n\)。可以证明这样操作之后必然可以覆盖所有边,大概证明方式就是,对于每一条边 \((i,j)\),显然恰好存在两个点 \(s\) 满足从 \(s\) 开始能够遍历到这个边,而这两个 \(s\) 刚好差 \(\dfrac{n}{2}\),因此必定恰好有一个 \(s\) 在 \([1,\dfrac{n}{2}]\) 中。具体细节留给读者自己思考。操作次数 \(\dfrac{k}{2}·(\dbinom{2n/k}{2}+\dfrac{2n}{2k})=\dfrac{k}{2}·(\dfrac{n}{k}·(\dfrac{2n}{k}-1)+\dfrac{n}{k})=\dfrac{n^2}{k}\),可以通过 hard version。
const int MAXN=1<<10;
int n,k,siz,cnt,is[MAXN+5];
bool query(int x){
printf("? %d\n",x);fflush(stdout);
char c;cin>>c;return (c=='Y');
}
void deal(int x){
for(int i=(x-1)*siz+1;i<=x*siz;i++) if(is[i]&&query(i)) is[i]=0;
}
int main(){
scanf("%d%d",&n,&k);siz=max(k>>1,1);cnt=n/siz;
for(int i=1;i<=n;i++) is[i]=1;
for(int i=1;i<=n/k;i++){
int cur=0;
for(int j=1;j<=cnt;j++) deal((i+cur+cnt)%cnt+1),cur=(cur<=0)-cur;
printf("R\n");fflush(stdout);
} int sum=0;for(int i=1;i<=n;i++) sum+=is[i];
printf("! %d\n",sum);fflush(stdout);
return 0;
}
Codeforces 1290D - Coffee Varieties(分块暴力+完全图的链覆盖)的更多相关文章
- Codeforces Beta Round #13 E. Holes 分块暴力
E. Holes Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/13/problem/E Des ...
- Codeforces#86D Powerful array(分块暴力)
Description An array of positive integers a1, a2, ..., an is given. Let us consider its arbitrary su ...
- Codeforces Round #616 Coffee Varieties
题意 不太容易讲清,看英文吧 codeforces 做法 先从简单的看起 将块以\(\frac{k}{2}\)个元素为界,然后类似线段树一样递归下去,每次一层的左子树跟右子树的块相互暴力比较 \[\b ...
- (分块暴力)Time to Raid Cowavans CodeForces - 103D
题意 给你一段长度为n(1 ≤ n ≤ 3·1e5)的序列,m (1 ≤ p ≤ 3·1e5)个询问,每次询问a,a+b,a+2b+...<=n的和 思路 一开始一直想也想不到怎么分,去维护哪些 ...
- D. Alyona and a tree 公式转换 + 分块暴力
http://codeforces.com/problemset/problem/740/D 对于每一对<u, v>.设dis[u]表示root到点u的距离,那么dis<u去v> ...
- Codeforces 1129D - Isolation(分块优化 dp)
Codeforces 题目传送门 & 洛谷题目传送门 又独立切了道 *2900( 首先考虑 \(dp\),\(dp_i\) 表示以 \(i\) 为结尾的划分的方式,那么显然有转移 \(dp_i ...
- Codeforces Gym 100015H Hidden Code 暴力
Hidden Code 题目连接: http://codeforces.com/gym/100015/attachments Description It's time to put your hac ...
- Codeforces gym 100685 A. Ariel 暴力
A. ArielTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100685/problem/A Desc ...
- Codeforces Gym 100637G G. #TheDress 暴力
G. #TheDress Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100637/problem/G ...
随机推荐
- 【数据结构与算法Python版学习笔记】树——相关术语、定义、实现方法
概念 一种基本的"非线性"数据结构--树 根 枝 叶 广泛应用于计算机科学的多个领域 操作系统 图形学 数据库 计算机网络 特征 第一个属性是层次性,即树是按层级构建的,越笼统就越 ...
- Java序列元素替换
1.数组 直接赋值. 2.String (1) String是不可变的,只能将新的字符串重新赋给String变量.可使用substring进行拼接: String s="hello" ...
- UltraSoft - Beta - Scrum Meeting 3
20200519会议纪要 Date: May 19th, 2020. Scrum 情况汇报 进度情况 组员 负责 今日进度 q2l PM.后端 暂无 Liuzh 前端 暂无 Kkkk 前端 完成了前端 ...
- Linux中检查字符串是否为合法IP地址的shell脚本
#!/bin/bash #判断IP地址是否为有效IP CHKECK_IP () { CHECK_STEP1=`echo $1 | awk -F"." '{print NF}'` i ...
- FastAPI 学习之路(五十五)操作Redis
之前我们分享了操作关系型数据库,具体文章, FastAPI 学习之路(三十二)创建数据库 FastAPI 学习之路(三十三)操作数据库 FastAPI 学习之路(三十四)数据库多表操作 这次我们分享的 ...
- C++ map操作——插入、查找、遍历
c++ map 操作学习 #include <iostream> #include <map> #include <string> #include <vec ...
- 数组中出现次数超过一半的数字 牛客网 剑指Offer
数组中出现次数超过一半的数字 牛客网 剑指Offer 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}.由于数字 ...
- cf2A Winner(implementation)
题意: N个回合. 每个回合:name score[名为name的这个人得了score分(可负可正)]. 问最后谁的累积分数是最高的.设为M.如果有好几个都得了M,找出这几个人中哪个最早回合累积分数超 ...
- Vue3学习(十)之 页面、菜单、路由的使用
一.前言 好几天没更文了,周末真的太冷了,在家躺了一天不爱动.今天给暖气了,相对不那么冷了,就可以继续更文了. 由文章标题不难看出,就是实现点击菜单跳转的意思,我写的很直白了,哈哈. 二.实现点击菜单 ...
- 『学了就忘』Linux基础命令 — 19、目录操作的相关命令
目录 1.ls命令 2.cd命令 (1)绝对路径和相对路径 (2)cd命令的简化用法 3.pwd命令 4.mkdir命令 5.rmdir命令 常用目录操作的相关命令: ls命令 cd命令 pwd命令 ...