【题解】CF1336D 题解
CF1336D 题解
思路分析
推式子的题,细节多。
为方便后续表述,下面定义一些东西:
- \(a_i\) 代表在初始的集合 \(S\) 中 \(i\) 的个数。
- “三同”代表原题中的 \(\text{triplet}\)。
- “三连“代表原题中的 \(\text{straight}\)。
采用兔队的构造方法,膜拜兔队!
STEP 1:分析插入后三同的变化量
三同的计算公式就是从每个数中任意选三个出来。
也就是下面这个式子:
其中 \(x\) 表示当前的集合中,每个数的个数。
假设插的是 \(i\)。
由于只有 \(x_i\) 加了 \(1\),因此变化量为 \(\text{C}^3_{x_i+1}-\text{C}^3_{x_i}\)。
推导一下式子:
于是有如下结论:
-
当 \(a_i\) 大于等于二时,那么就可以往里面插入一个 \(i\),然后根据变化量来计算具体的值。
-
当然,此时如果 \(a_i\) 小于二,那么插入后这个变化量就是零。此时无法确定精确的值。
无论如何,我们都要往 \(1\) 到 \(n-1\) 里各插入一个。
那么为什么不往 \(n\) 里面插入呢?这就涉及到三连的问题了。因为到此时,我们还没使用三连的变化量。
STEP 2:分析插入后三连的变化量
还是假设我们要插 \(i\)。
根据上述结论,我们可以肯定,\(1\) 到 \(i - 1\) 都插了一个。
然后就可以开始推式子了,这里为了方便,以 \(n=7\),插入 \(5\) 为例。
原来的三连:
插入 \(5\) 后:
注意观察含有 \(a_5+1\) 的那几项,于是我们可以算出变化量为:
一般化:
那么我们只要知道了 \(1\) 到 \(i-1\) 的所有 \(a\) 的值,根据前面三连的变化量,即可算出 \(a_i\)。注意 \(i\) 必须大于等于 \(4\)。
那么我们就可以回答上面的问题了:\(n\) 可以根据三连的变化量推出来,不用单独询问计算。
怎么推?非常简单,我们直接看 \(n-1\) 的三连变化量就可以了,它的值为:
直接算就可以了。
那么这里又引出了另一个问题,万一有值无法确定怎么办?
STEP 3:根据三连的变化量确定小于二的值
假设 \(a_i\) 无法根据三同确定。
参考推 \(n\) 的方法,去看 \(i-1\) 的三连变化量。
它的变化量为:
注意这时候我们还有个条件就是 \(a_i<2\),因为只有这样才无法根据三同确定。
假设 \(i-1\) 的三连变化量为 \(g\),我们可以分类讨论:
- 如果 \(g-(a_{i-3}+1)(a_{i-2}+1)\) 不为零,说明 \(a_i\) 一定不是零,这样 \(a_i\) 就只能是一。
- 如果 \(g-(a_{i-3}+1)(a_{i-2}+1)\) 为零,由于 \(a_{i-2}\geq 0\),\(a_{i+1}\geq 0\),所以 \(a_{i-2}+1+a_{i+1}\geq 1\)。因此 \(a_i\) 为零。
这样我们就把小于二的值给确定了。
但是需要注意,上述结论仅在 \(i\geq 4\) 时成立。
这里又引出一个问题:\(i\leq 3\) 时怎么确定?
STEP 4:确定余下的值
基本上做完了,但是我们还需要确定出 \(i \leq 3\) 时,小于二的 \(a_i\) 的值。
怎么算?这里我直接给出结论:再插一个 \(1\)。
根据三同的变化量公式,此时我们必然能确定 \(a_1\) 的值,无论它的大小是多少。
那么我们就去考虑 \(a_2\) 和 \(a_3\)。
注意到再插入一个 \(1\) 之后,他三连的变化量为 \((a_2+1)(a_3+1)\)。
同时我们还知道第一次插 \(1\) 的时候,三连的变化量为 \(a_2a_3\)。
于是根据上面两个变化量,我们可以算出 \(a_2+a_3\) 的值,不妨设其为 \(t\)。
那么现在,如果在 \(a_2\) 和 \(a_3\) 中,有一个可以根据三同确定,那另一个也就确定了,所以我们就只用考虑 \(a_2\) 和 \(a_3\) 均小于二的情况。
分类:
- \(t=0\),都是 \(0\)。
- \(t=2\),都是 \(1\)。
- \(t=1\),看插 \(2\) 的三连变化量,为 \((a_1+1)a_3+a_3a_4=a_3(a_1+a_4+1)\)。那么如果变化量为 \(0\),由于 \(a_{1}\geq 0\),\(a_{4}\geq 0\),那么 \(a_1+a_4+1 \geq 1\),也就是说 \(a_3=0\),那么 \(a_2=1\)。反之,如果变化量不为 \(0\),那么 \(a_3=1\),\(a_2=0\)。
于是就可以确定所有的值了!
代码
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int lastt, lastl; //前一个的三同和三连
int nowt, nowl; //现在的三同和三连
int dt[N]; //三同的变化量
int dl[N]; //三连的变化量
int a[N]; //答案
//根据 C(k,2) 推出 k
int orzscz(int k)
{
return (sqrt(8 * k + 1) + 1) / 2;
}
int main()
{
int n;
cin >> n;
cin >> lastt >> lastl; //上一个三同和三连
for(int i = 1;i < n;i++) //插 1 到 n - 1
{
cout << "+ " << i << endl;
cin >> nowt >> nowl;
dt[i] = nowt - lastt; //三同变化量
dl[i] = nowl - lastl; //三连变化量
if(dt[i]) a[i] = orzscz(dt[i]); //根据三同尝试确定
lastt = nowt;
lastl = nowl;
}
cout << "+ 1" << endl; //最后一个插 1
cin >> nowt >> nowl;
dt[n] = nowt - lastt;
dl[n] = nowl - lastl;
if(dt[n]) a[1] = orzscz(dt[n]) - 1; //根据三同推出,因为多插了一个 1,所以要减去一
int sum = dl[n] - dl[1] - 1; //a_2+a_3 的值
if(a[2]) a[3] = sum - a[2]; //如果已知a[2]就可算a[3]
else if(a[3]) a[2] = sum - a[3]; //如果已知a[3]就可算a[2]
else //都不知道
{
if(sum == 0) a[2] = a[3] = 0; //是零就零
else if(sum == 2) a[2] = a[3] = 1; //是二就两个一
else //是一
{
//看插2的三连变化量
if(dl[2])
{
a[2] = 0;
a[3] = 1;
}
else
{
a[2] = 1;
a[3] = 0;
}
}
}
for(int i = 4;i < n;i++)
{
if(!a[i])
{
//根据推出的公式确定无法确定的值
int t = dl[i - 1] - (a[i - 3] + 1) * (a[i - 2] + 1);
if(t) a[i] = 1;
else a[i] = 0;
}
}
a[n] = (dl[n - 1] - (a[n - 3] + 1) * (a[n - 2] + 1)) / (a[n - 2] + 1); //根据公式计算 a_n 的值
//输出答案
cout << "! ";
for(int i = 1;i <= n;i++)
{
cout << a[i] << " ";
}
cout << endl;
return 0;
}

浙公网安备 33010602011771号