引言

思维题。

这个做法跑得飞快,还不用 dp,也不是爆搜!

复杂度(可能)为 \(O(s^2t)\) 或 \(O(s^2)\),实际效率也是飞快。

不过这题我直接提交答案了。

思路

考虑 \(A=mn,B=m+n\)。

假设 \(A\) 先手。

从 \(A\) 中枚举分解方案,假设有 \((m_1,n_1)(m_2,n_2)\cdots(m_k,n_k)\) 这些合法。

如 \(k>1\),会说不知道,称这样的 \(A\) 构成集合 \(A_{>0}\);否则知道,称这样的 \(A\) 构成集合 \(A_0\)。

对 \(B\) 分解,其有 \((s,B-s)(s+1,B-s-1)\cdots(\lfloor B/2\rfloor,\lceil B/2\rceil)\) 这些合法。

从中选出相乘为 \(A_0\) 中元素的数对,若唯一,记这样的 \(B\) 构成 \(B_0'\),说明知道;若多于 \(1\) 个,则无法计算;若不存在,若解集大小为 \(1\),则构成集合 \(B_0\),说明知道;若解集大小大于 \(1\),构成集合 \(B_{>0}\)。

对 \(B_0\) 集合类似地反推 \(A_1'\),对 \(B_{>0}\) 反推出 \(A_1,A_{>1}\)。

刚才的描述不够清晰,让我们形式化地说:

\[\def\defeq{\mathop{=}\limits^{\operatorname{def}}}
\begin{array}{|l|}
\hline
X(A)\defeq\{(m,n)|s\le m\le n,mn=A\}\\
Y(B)\defeq\{(m,n)|s\le m\le n,m+n=B\}\\
A_{\ge0}\defeq\{A|X(A)\neq\varnothing\}\\
B_{\ge0}\defeq\{B|Y(B)\neq\varnothing\}\\\hdashline
A_0\defeq\{A|A\in A_{\ge0},|X(A)|=1\}\\
A_{\ge1}\defeq A_{\ge0}-A_0\\\hdashline
X_0\defeq\cup_{A\in A_0}X(A)\\
B_0'\defeq\{B|B\in B_{\ge0},|Y(B)\cap X_0|=1\}\\
B_0\defeq\{B|B\in B_{\ge0},|Y(B)-X_0|=1\}\\
B_{\ge1}\defeq\{B|B\in B_{\ge0},|Y(B)-X_0|>1\}\\\hdashline
Y_0\defeq\cup_{B\in B_0}Y(B)\\
A_1'\defeq\{A|A\in A_{\ge1},|X(A)\cap Y_0|=1\}\\
A_1\defeq\{A|A\in A_{\ge1},|X(A)-Y_0|=1\}\\
A_{\ge2}\defeq\{A|A\in A_{\ge1},|X(A)-Y_0|>1\}\\\hdashline
X_1\defeq\cup_{A\in A_1}X(A)\\
B_1'\defeq\{B|B\in B_{\ge1},|Y(B)\cap X_1|=1\}\\
B_1\defeq\{B|B\in B_{\ge1},|Y(B)-X_1|=1\}\\
B_{\ge2}\defeq\{B|B\in B_{\ge1},|Y(B)-X_1|>1\}\\\hdashline
\cdots\\
\hline
\end{array}
\]

查询 \(t=0\),就是查询 \(A_0/B_0'\);查询 \(t=1\),就是查询 \(B_0/A_1'\);查询 \(t=2\),就是查询 \(A_1/B_1'\);等等。

\(A_{\ge1}\) 就是 \(A\) 报了一次不知道后的可能集合;\(B_{\ge1}\) 就是 \(B\) 报了一次不知道后的可能集合;\(A_{\ge2}\) 就是 \(A\) 报了两次不知道后的可能集合;等等。

B 先手同理。

试看看!

你已经学会基本的思考方法了,让我们来做一些小练习吧!

实战一些数据。

测试点 \(2\)

1 Alice 2

对,先 \(2\) 再 \(1\)。

主要是因为啊,这个东西嘛,和我们刚刚说的一样,是 A 先手,不用重新转换视角。

对这种东西,我们可以考虑对下面的东西列表格:

\[f(A)=\max\{k|A\in A_{\ge k}\}
\]
\[g(B)=\max\{k|B\in B_{\ge k}\}
\]

这样的手算会简单一点。(?)

\[\begin{matrix}
\hline
n&1&2&3&4&5&6&7
\\\hline\hline
f(n)&0&0&0&1&0&\ge1&0
\\\hline
g(n)&-1&0&0&0&\ge1&\ge1&\ge1
\\\hline
\end{matrix}
\]

因此 \(m=1,n=4\) 最优。

测试点 \(1\)

1 Bob 2

反过来枚举顺序,即得如下。

\[\begin{matrix}
\hline
n&1&2&3&4&5&6&7
\\\hline\hline
f(n)&0&0&0&\ge1&0&\ge1&0
\\\hline
g(n)&-1&0&0&1&\ge1&\ge1&\ge1
\\\hline
\end{matrix}
\]

\(m=n=2\) 最优。

测试点 \(3\)

2 Bob 2

类似,但是要手枚更多项。

\[\begin{matrix}
\hline
n&1&2&3&4&5&6&7&8&9&10&11&12
\\\hline\hline
f(n)&-1&-1&-1&0&-1&0&-1&0&0&0&-1&\ge1
\\\hline
g(n)&-1&-1&-1&0&0&1&1&\ge1&\ge1&&&
\\\hline
\end{matrix}
\]

\(m=3,n=4\) 最优。

注意不是 \(2,4\)——\(8\) 已经置 \(0\) 了!

测试点 \(4\)

11 Bob 2

已经不能指望手算了——刚刚的那组数据都很困难。

考虑代码实现以上过程。

\(t=2\) 时,\(B=m+n\) 在 \(4s\) 内较有可能,考虑仅计算 \(mn\le4s^2+100,m+n\le5s+100\) 的部分解集。

首先提取范围内的 \(A_{\ge0}\) 与 \(B_{\ge0}\),并算出对应的 \(X(A),Y(B)\)。

然后枚举 \(B_0\),得到 \(B_{\ge1}\)。

枚举 \(A_0\),得到 \(A_{\ge1}\)。

枚举 \(B_1\),进而得解。

成功地,给出了解 \(n=15,m=16\)。

测试点 \(5\)

18 Bob 2

这个给出的解为 \(n=21,m=24\),交了一下是错的,是不是我们给的界不够大?

不是,其实是因为,我们没有再校验 \(A_1'\)!

在从 \(B_1\) 推断 \(A_1'\) 后,我们还要校验其合法性:\(A_1'\) 不一定可以被唯一决策!

验证完后即得正解 \(n=20,m=27\)。

测试点 \(6\sim10\)

28 Bob 2
28 Alice 2
57 Alice 2
111 Alice 2
200 Alice 2

把从 \(A\) 出发的情况实践一下 容易依次得解。

这样我们就解决了 \(t=2\) 的部分(测试点 \(1\sim10\)),答案依次为

2 2
1 4
3 4
15 16
20 27
35 40
28 45
65 72
114 140
200 242

可以拿到 \(\rm40pts\)。

以下是暴力代码。

int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
static chr Op[50];
uint s,t;scanf("%u%s%u",&s,Op,&t);
if(t!=2)exit(0);
static std::vector<std::pair<uint,uint> >X[1000005],Y[1000005];
uint Lim1=4*s*s,Lim2=5*s;
for(uint m=s;m<=Lim2;m++)for(uint n=m;n*m<=Lim1&&n+m<=Lim2;n++){
X[n*m].push_back({m,n});
Y[n+m].push_back({m,n});
}
static bol Xi[1000005],Yi[1000005];
std::vector<uint>Ag,Bg;
for(uint i=1;i<=Lim1;i++)if(X[i].size())Ag.push_back(i),Xi[i]=1;
for(uint i=1;i<=Lim2;i++)if(Y[i].size())Bg.push_back(i),Yi[i]=1;
if(Op[0]=='B'){
std::vector<uint>User;
User.clear();
for(auto b:Bg){
uint c=0;
for(auto g:Y[b])c+=Xi[g.first*g.second];
if(c>=2)User.push_back(b);
else Yi[b]=0;
}
Bg=User,User.clear();
for(auto a:Ag){
uint c=0;
for(auto g:X[a])c+=Yi[g.first+g.second];
if(c>=2)User.push_back(a);
else Xi[a]=0;
}
Ag=User,User.clear();
for(auto b:Bg){
uint c=0;
for(auto g:Y[b])c+=Xi[g.first*g.second];
if(c==1)User.push_back(b);
else Yi[b]=0;
}
for(auto b:User)for(auto g:Y[b])if(Xi[g.first*g.second]){
uint c=0;
for(auto p:X[g.first*g.second])c+=Yi[p.first+p.second];
if(c==1){
printf("%u %u\n",g.first,g.second);
return 0;
}
}
}
else{
std::vector<uint>User;
User.clear();
for(auto a:Ag){
uint c=0;
for(auto g:X[a])c+=Yi[g.first+g.second];
if(c>=2)User.push_back(a);
else Xi[a]=0;
}
Ag=User;
User.clear();
for(auto b:Bg){
uint c=0;
for(auto g:Y[b])c+=Xi[g.first*g.second];
if(c>=2)User.push_back(b);
else Yi[b]=0;
}
Bg=User,User.clear();
for(auto a:Ag){
uint c=0;
for(auto g:X[a])c+=Yi[g.first+g.second];
if(c==1)User.push_back(a);
else Xi[a]=0;
}
uint x=-1,y=-1;
for(auto a:User)for(auto g:X[a])if(Yi[g.first+g.second]){
uint c=0;
for(auto p:Y[g.first+g.second])c+=Xi[p.first*p.second];
if(c==1){
if(g.first+g.second<x+y||(g.first+g.second==x+y&&g.first<x))
x=g.first,y=g.second;
}
}
printf("%u %u\n",x,y);
}
return 0;
}

测试点 \(11\sim25\)

看下面 \(5\) 组数据(\(11\sim15\))。

1 Bob 3
69 Alice 3
147 Alice 4
88 Alice 5
109 Bob 6

考虑到刚刚的做法,其不能进一步应用于 \(t>2\),主要是因为我们无法确定答案的值域,刚刚的做法是挂掉的(必须得有数目足够多的元素在外围“盖住”当前的答案,使得不会有更小解被误选择)。

我们猜测实际值域不会很大,尝试把刚才的过程再做几轮,试着跑一跑?

这部分代码如下:

int main()
{
#ifdef MYEE
freopen("QAQ.in","r",stdin);
// freopen("QAQ.out","w",stdout);
#endif
static chr Op[50];
uint s,t;scanf("%u%s%u",&s,Op,&t);
if(t>6)exit(0);
static std::vector<std::pair<uint,uint> >X[1000005],Y[1000005];
uint Lim1=4*s*s+100,Lim2=5*s+100;
for(uint m=s;m<=Lim2;m++)for(uint n=m;n*m<=Lim1&&n+m<=Lim2;n++){
X[n*m].push_back({m,n});
Y[n+m].push_back({m,n});
}
static bol Xi[1000005],Yi[1000005];
std::vector<uint>Ag,Bg;
for(uint i=1;i<=Lim1;i++)if(X[i].size())Ag.push_back(i),Xi[i]=1;
for(uint i=1;i<=Lim2;i++)if(Y[i].size())Bg.push_back(i),Yi[i]=1;
std::vector<uint>User;
for(uint i=0;i<t;i++)if(!(i&1)==(*Op=='B')){
for(auto b:Bg){
uint c=0;
for(auto g:Y[b])c+=Xi[g.first*g.second];
if(c>=2)User.push_back(b);
else Yi[b]=0;
}
Bg=User,User.clear();
}
else{
for(auto a:Ag){
uint c=0;
for(auto g:X[a])c+=Yi[g.first+g.second];
if(c>=2)User.push_back(a);
else Xi[a]=0;
}
Ag=User,User.clear();
}
if((t&1)==(*Op=='B')){
for(auto a:Ag){
uint c=0;
for(auto g:X[a])c+=Yi[g.first+g.second];
if(c==1)User.push_back(a);
else Xi[a]=0;
}
uint x=-1,y=-1;
for(auto a:User)for(auto g:X[a])if(Yi[g.first+g.second]){
uint c=0;
for(auto p:Y[g.first+g.second])c+=Xi[p.first*p.second];
if(c==1){
if(g.first+g.second<x+y||(g.first+g.second==x+y&&g.first<x))
x=g.first,y=g.second;
}
}
printf("%u %u\n",x,y);
}
else{
for(auto b:Bg){
uint c=0;
for(auto g:Y[b])c+=Xi[g.first*g.second];
if(c==1)User.push_back(b);
else Yi[b]=0;
}
for(auto b:User)for(auto g:Y[b])if(Xi[g.first*g.second]){
uint c=0;
for(auto p:X[g.first*g.second])c+=Yi[p.first+p.second];
if(c==1){
printf("%u %u\n",g.first,g.second);
return 0;
}
}
}
return 0;
}

依次分别得到

1 4
80 84
162 170
100 110
126 128

似乎……也不是很大?

交一下……是对的!

胆子放大点,继续来做 \(16\sim20\)!

把代码改一下,运行

4 Bob 7
117 Alice 8
161 Alice 9
134 Alice 10
77 Bob 11

得到输出

4 12
128 135
182 184
135 176
78 108

还是很小啊!

直接把剩下的(\(21\sim25\))都跑一遍。

177 Bob 12
178 Bob 13
179 Bob 14
180 Bob 15
178 Alice 15

得到输出

185 216
180 222
192 210
180 224
196 208

直接就过了!!!

总复杂度不会证明,但应该是 \(O(s^2t)\) 或 \(O(s^2)\) 的。

Code

最终代码不给了,把上面那个 \(t\le6\) 的代码改一改就是了。

提交答案题一份代码速通的艺术是怎样的啊。

loj2511的更多相关文章

随机推荐

  1. 时隔3个月,Uber 再遭数据泄露...

    在今年9月,Uber 就发生过一起数据泄露事件,尽管黑客并无意发动大规模攻击或以此来获取巨额利益,但其成功获取对 Uber 所有敏感服务的完全管理员访问权限仍令人后怕.而在上周,名为"Ube ...

  2. echarts去除下载小图标

    toolbox: { show: true, orient: 'vertical', left: 'right', top: 'center', feature: { dataView: { read ...

  3. 外包公司中的"炼狱",极度摧残,避免踩坑。

    引言 前些天羊了,一直没有更新.今天给大家聊聊两家外包公司,遇到的小伙伴避免踩坑. 咱不说那些虚的,什么尽量不要去外包公司,尽可能找甲方,这些谁都知道,肯定是因为一些原因(比如学历.项目经验.技术有待 ...

  4. vite+ts+vue3+router4+Pinia+ElmPlus+axios+mock项目基本配置

    1.vite+TS+Vue3 npm create vite Project name:... yourProjectName Select a framework:>>Vue Selec ...

  5. .Net 7 的AOT的程序比托管代码更容易破解?

    楔子 .Net 7的一个重要功能是把托管的源码编译成Native Code,也就是二进制文件.此举看似增加了程序反编译难度,实际上是减少了程序的破解难度.本篇在不触及整个程序架构的前提下,以简单的例子 ...

  6. [OpenCV实战]2 人脸识别算法对比

    在本教程中,我们将讨论各种人脸检测方法,并对各种方法进行比较.下面是主要的人脸检测方法: 1 OpenCV中的Haar Cascade人脸分类器: 2 OpenCV中的深度学习人脸分类器: 3 Dli ...

  7. Java入门及环境搭建

    1.JAVA三大版本 JAVASE(标准版:桌面程序开发.控制台开发...) JAVAME(嵌入式:手机程序.小家电...) JAVAEE(企业级:web端.服务器开发...) 2.开发环境 JDK: ...

  8. 使用linux命令直接在网上下载文件,解压,改名

    举例: 我们想要在服务器某个路径下,下载一个node.js包 操作如下 假如文件地址为https://npm.taobao.org/mirrors/node/v16.9.1/node-v16.9.1- ...

  9. CTFshow刷题记录

    整理的一些ctf题目 WEB题 ctfshow 年CTF 除夕 题目要求通过get传入year参数然后进行判断是否成立,成立就返回flag 这里可以用科学计数法表示通过get传入year=2.022e ...

  10. 一小时学会微信小程序

    一小时学会微信小程序 目录 一.小程序概要 1.1.发展历史 1.2.小程序的诞生 二.微信小程序介绍 三.开发微信小程序的软件下载与初步配置  3.1.获取微信小程序的AppID 3.2.下载安装微 ...