0%

题目大意

给定长度为nn的数组aa。对于整数kk,我们称其为piggy,当且仅当通过执行以下操作任意次数(可能为零),可以将aa以非递减顺序排序:

  • 首先,选择两个索引iijj1i<jn1 \le i < j \le n),使得aiajk|a_i - a_j| \ge k;

  • 然后,交换aia_iaja_j

您需要确定最大的piggy整数kk。如果不存在这样的整数,则输出1-1

思路讲解

不难注意到,我们可以选用最大的数字或者最小的数字去做中转站,

image

乍一看,好像选择最小的数字,还是选择最大的数字,取决于两个数字 totonownow?实际上不是的,我们完全可以换着来,比如说:

image

这样子就实现了 totomnmnnownow 用这个 mxmx,反之亦然,完全可以只根据一个数去选择

okay,那就行了。我们发现,这个 totonownow 一定都是和有序序列中的同位置值不一样的点,因此,我们的答案其实就是:(其中 SS 为所有和有序序列中的同位置值不一样的点的下标集合)

ansminiS(max(mxai,aimn))ans\gets\min_{i\in S}(\max(mx-a_i,a_i-mn))

当然,也可以使用代码中的这个贪心实现。

AC代码

AC
https://codeforces.com/contest/2188/submission/360594620

心路历程(WA,TLE,MLE……)

题目大意

给定两个非负整数 xxyy。找到两个非负整数ppqq,使得p  &  q=0p\;\&\;q=0,且xp+yq|x-p|+|y-q|最小化。这里,&\&表示按位与操作

You are given two non-negative integers xx, yy. Find two non-negative integers pp and qq such that p  &  q=0p\;\&\;q=0, and xp+yq|x-p|+|y-q| is minimized. Here, &\& denotes the bitwise AND operation.

Input

Each test contains multiple test cases. The first line contains the number of test cases tt (1t1041 \le t \le 10^4). The description of the test cases follows.

The only line of each test case contains two non-negative integers xx and yy (0x,y<2300 \le x,y < 2^{30}).

Output

For each test case, output two non-negative integers pp and qq you found. If there are multiple pairs of pp and qq satisfying the conditions, you may output any of them.

It can be proven that under the constraints of the problem, any valid solution satisfies max(p,q)<231\max(p,q)<2^{31}.

1
2
3
4
5
6
7
8
7
0 0
1 1
3 6
7 11
4 4
123 321
1073741823 1073741822
1
2
3
4
5
6
7
0 0
2 1
3 8
6 9
4 3
128 321
1073741824 1073741822

Note

In the first test case, one valid pair would be p=0p=0 and q=0q=0, as 0&0=00\,\&\,0=0 and xp+yq=00+00=0|x-p|+|y-q|=|0-0|+|0-0|=0 is the minimum over all pairs of pp and qq.

In the third test case, one valid pair would be p=3p=3 and q=8q=8, as 3&8=03\,\&\,8=0 and xp+yq=33+86=2|x-p|+|y-q|=|3-3|+|8-6|=2 is the minimum over all pairs of pp and qq. Note that (p,q)=(3,4)(p,q)=(3,4) is also a valid pair.

思路讲解

我们需要利用这个高位较优的这个特性,

我们设计了一个 dp,dpi,0dp_{i,0} 表示该位采用 0,00,0 的配置,dpi,1dp_{i,1} 表示该位采用 1,01,0 的配置,dpi,2dp_{i,2} 表示该位采用 0,10,1 的配置。

不难写出这样的转移式子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vector dp(40,vector<Diff_p_q>(3));
dp[36][0]=Diff_p_q(0,0);
dp[36][1]=Diff_p_q(1ll<<36,0);
dp[36][2]=Diff_p_q(0,1ll<<36);
for (int i=35;i>=0;--i) {
dp[i]=dp[i+1];
// dp[i][0] 自己重新开始 继承 继承 继承
dp[i][0]=min({Diff_p_q(0,0),dp[i][0],dp[i+1][1],dp[i+1][2]});
// 自己重新开始
dp[i][1]=min({Diff_p_q(1ll<<i,0),Diff_p_q(dp[i+1][0].p+(1ll<<i),dp[i+1][0].q),
Diff_p_q(dp[i+1][1].p+(1ll<<i),dp[i+1][1].q),
Diff_p_q(dp[i+1][2].p+(1ll<<i),dp[i+1][2].q)});
dp[i][2]=min({Diff_p_q(0,1ll<<i),Diff_p_q(dp[i+1][0].p,dp[i+1][0].q+(1ll<<i)),
Diff_p_q(dp[i+1][1].p,dp[i+1][1].q+(1ll<<i)),
Diff_p_q(dp[i+1][2].p,dp[i+1][2].q+(1ll<<i))});
}

然后你忐忑地提交了,结果 WA on 2。

你使用了对拍,或者其实你不需要使用对拍,你也知道,是因为上面的做法有一些激进,高位上比较好,那自然是比较好的,但是好分大的好和小的好。那么上面的做法,大的好,自然是可以得到的,但是小的好,就不是那么简单的可以得到的了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
_______________[ Testcase #1 INPUT ]_______________
1
78 88

_______________[ Testcase #1 OUTPUT]_______________
78 128
WA


[-] FAILURE: RUNTIME ERROR
Ans.diff:40
brute_diff:39
x_br:39
y_br:88

那么对拍也给了我们这个反馈,确实是这样的。

但是我们怎么样得到小的好呢?其实也简单,我们使用同样方法,整一个 dp,但是我们 dp 里存的的那个结构体的构造函数,我们传入一个指示参数,使得只要一出现大于的情况,就给他附上 diffINFdiff\gets\text{INF},使其不够优秀而被淘汰,强制得到这个我们所谓的小的好

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Diff_p_q {
ll diff,p,q;
bool operator<(const Diff_p_q &o) const {
return diff<o.diff;
}
Diff_p_q()=default;
explicit Diff_p_q(ll p,ll q,bool no_big=false) {
if (!no_big) {
diff=abs(p-X)+abs(q-Y);
this->p=p;
this->q=q;
}else {
if (p > X || q > Y) {
diff=INF;
}else {
diff=abs(p-X)+abs(q-Y);
}
this->p=p;
this->q=q;
}

}
};

一样的 dp 过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Diff_p_q Ans=min({dp[0][0],dp[0][1],dp[0][2]});
{
vector dp2(40,vector<Diff_p_q>(3));
dp2[36][0]=Diff_p_q(0,0,true);
dp2[36][1]=Diff_p_q(1ll<<36,0,true);
dp2[36][2]=Diff_p_q(0,1ll<<36,true);
for (int i=35;i>=0;--i) {
dp2[i]=dp2[i+1];
// dp[i][0] 自己重新开始 继承 继承 继承
dp2[i][0]=min({Diff_p_q(0,0,true),dp2[i][0],dp2[i+1][1],dp2[i+1][2]});
// 自己重新开始
dp2[i][1]=min({Diff_p_q(1ll<<i,0,true),Diff_p_q(dp2[i+1][0].p+(1ll<<i),dp2[i+1][0].q,true),
Diff_p_q(dp2[i+1][1].p+(1ll<<i),dp2[i+1][1].q,true),
Diff_p_q(dp2[i+1][2].p+(1ll<<i),dp2[i+1][2].q,true)});
dp2[i][2]=min({Diff_p_q(0,1ll<<i,true),Diff_p_q(dp2[i+1][0].p,dp2[i+1][0].q+(1ll<<i),true),
Diff_p_q(dp2[i+1][1].p,dp2[i+1][1].q+(1ll<<i),true),
Diff_p_q(dp2[i+1][2].p,dp2[i+1][2].q+(1ll<<i),true)});
}
Ans=min({Ans,dp2[0][0],dp2[0][1],dp2[0][2]});
}
cout<<Ans.p<<" "<<Ans.q<<"\n";

okay,上面我们已经把我们的思路讲的比较清楚了。

我感觉这个代码对是因为,在确定好高位以后,绝对值距离最小的数一定贴在区间边界:要么贴上边界(低位全 0),要么贴下边界(低位全 1);中间形态永远不可能比边界更近。

不过我们的第一种 dp 由于没有引入这个 loose 和 tight,实际上计算出来的,并不是严格的 ,会有 << 的情况出现,所以我心里也不太踏实。欢迎 hack 我的代码。

AC代码

AC
https://codeforces.com/contest/2188/submission/360662700

心路历程(WA,TLE,MLE……)

题目大意

这是问题的简单版本。版本之间的区别在于xx的约束;在这个版本中,x105x \le 10^5

Polycarp有一个包含从11101210^{12}的所有自然数的序列。他决定通过执行以下操作$ x $次来修改这个序列:

  • 同时删除所有在位置yy2y2 \cdot y3y3 \cdot y,…,mynm \cdot y \le n的数字,其中$ n $是当前序列的长度。

之后,Polycarp想要在剩余序列中找到第$ k 个数字,或确定结果序列的长度小于个数字,或确定结果序列的长度小于 k $。

帮助Polycarp解决这个问题!

考虑一个例子。假设$ x = 2 y = 3 k = 5 $,那么:

image

红线划掉的数字在第一次操作后被删除,蓝线划掉的数字在第二次操作后被删除。因此,位置$ k = 5 处的数字是数字处的数字是数字 10 $。

思路讲解


我们直接使用这个第一问当中的这个 check 函数,由于其是 O(X)O(X),会超时。我们也可以优化这个二分检查器代码,让其一次性把 subtractlenysubtract\gets \lfloor \frac{len}{y}\rfloor 相同的一起处理。这样子,使用二分的复杂度就是 O(AlogA)O(\sqrt A \log A),其中 A\sqrt A 是一个数字能够拥有的这个因数数量量级。

不过题目作者专门卡了我们二分答案一手,因此必须采用逆向还原的思路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (ll i=1;i<=X;++i) {
ll subtract=0;
if (len%(Y-1)==0) {
subtract=len/(Y-1)-1;
len+=subtract;
}else {
subtract=len/(Y-1);
ll dis=((Y-1)*(subtract+1))-len;
ll cnt=(dis+subtract-1)/subtract;
cnt=min(X-i+1,cnt);
i+=cnt-1;
len+=cnt*subtract;
}
if (len>(ll)1e12) {
cout<<-1<<"\n";
return;
}
}

逆向还原,我们知道,每出现 y1y-1 个数字,那么就代表有一个数字被删除了,那么一次需要增长的长度就是 leny1\lfloor \frac{len}{y-1} \rfloor

这里有一个比较阴的特殊情况,就是 len0(mody1)len \equiv 0 \pmod {y-1},这个时候,能够增长的长度就是只有 leny11\lfloor \frac{len}{y-1} \rfloor-1

我们可以就像最简单的,当序列长度为 y1y-1,其达到 y1y-1 需要多少长度,是 yy 吗?显然不是,其需要增长的就是 0 长度。

我们和优化二分的时候采用一样的思路,每次增长相同长度的,一起操作,这样子时间复杂度就是 O(A)O(\sqrt A)

具体而言,就是先计算需要增长的长度 subtractsubtract,然后计算需要能够以 subtractsubtract 继续增长的次数,即 cntcnt,然后 cntmin(cnt,Xi+1)cnt\gets \min(cnt,X-i+1),避免加上 cntcnt 超过操作次数限制。不断重复这个过程,直到达到操作次数 XX

AC代码

AC
https://codeforces.com/contest/2169/submission/360686378

TLE

https://codeforces.com/contest/2169/submission/360461585

心路历程(WA,TLE,MLE……)

题目大意

这是问题的简单版本。版本之间的区别在于xx的约束;在这个版本中,x105x \le 10^5

Polycarp有一个包含从11101210^{12}的所有自然数的序列。他决定通过执行以下操作$ x $次来修改这个序列:

  • 同时删除所有在位置yy2y2 \cdot y3y3 \cdot y,…,mynm \cdot y \le n的数字,其中$ n $是当前序列的长度。

之后,Polycarp想要在剩余序列中找到第$ k 个数字,或确定结果序列的长度小于个数字,或确定结果序列的长度小于 k $。

帮助Polycarp解决这个问题!

考虑一个例子。假设$ x = 2 y = 3 k = 5 $,那么:

image

红线划掉的数字在第一次操作后被删除,蓝线划掉的数字在第二次操作后被删除。因此,位置$ k = 5 处的数字是数字处的数字是数字 10 $。

思路讲解

二分答案。

思路参考了A_G 的代码

将这道题目转化为二分答案的关键,就在于把找到剩余的第 $ k $ 个数字,转化为找到一个长度为 lenlen 的初始序列,使得其剩余至少 kk 个数字,那么不难发现,最小的,符合我们的要求的 lenlen 就是答案。

那么,一次操作会减少多少个数字(长度为 lenlen 时)? 其实就是 leny\lfloor \frac{len}{y}\rfloor

因此,简单版本,检查函数也不难书写,直接模拟改过程即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
auto check=[&](ll len) {
ll cnt=1;
while (cnt<=X) {
if (len<=0) {
break;
}
len-=len/Y;
++cnt;
}
if (len>=K) {
return true;
}
return false;
};

AC代码

AC
https://codeforces.com/contest/2169/submission/360432707

心路历程(WA,TLE,MLE……)

题目大意

CF2169C Range Operation

题目描述

给定一个长度为 nn 的整数数组 aa

你可以进行以下操作:选择一个区间 [l,r][l, r]1lrn1 \le l \le r \le n),并将区间内的元素 al,al+1,,ara_l, a_{l+1}, \dots, a_r 全部替换为 l+rl + r

你的任务是计算,在最多允许进行一次上述操作的情况下,数组的和可能达到的最大值。

输入格式

第一行包含一个整数 tt1t1041 \le t \le 10^4),表示测试用例的数量。

每个测试用例的第一行包含一个整数 nn1n21051 \le n \le 2 \cdot 10^5)。

第二行包含 nn 个整数 a1,a2,,ana_1, a_2, \dots, a_n0ai2n0 \le a_i \le 2n)。

输入额外限制:所有测试用例中 nn 的总和不超过 21052 \cdot 10^5

输出格式

对于每个测试用例,输出一个整数,表示在至多进行一次操作后数组的最大和。

输入输出样例 #1

输入 #1

1
2
3
4
5
6
7
8
9
10
4
3
2 5 1
2
4 4
4
1 3 2 1
5
3 2 0 9 10

输出 #1

1
2
3
4
5
13
8
20
32

说明/提示

在第一个样例中,可以对子数组 [3,3][3,3] 执行操作,得到数组 [2,5,6][2, 5, 6],其和为 1313

在第二个样例中,不需进行任何操作。

在第三个样例中,可以对子数组 [1,4][1,4] 执行操作,得到数组 [5,5,5,5][5, 5, 5, 5],其和为 2020

在第四个样例中,可以对子数组 [2,3][2,3] 执行操作,得到数组 [3,5,5,9,10][3, 5, 5, 9, 10],其和为 3232

由 ChatGPT 5 翻译

思路讲解

类似于求解最大字段和的 dp。

思路参考了 starsilk 的代码。

首先,我们必须要把 r,lr,l 两者分开。

image

我们就会发现,总答案就可以写为:(SxS_{x} 表示 xx 的前缀和)

ans=Sl1+l(1l)+SnSr+r(r+1)ans=S_{l-1}+l(1-l)+S_n-S_{r}+r(r+1)

这个明显可以使用这个 dp,因为我们只需要最优化 Sl1+l(1l)S_{l-1}+l(1-l) 这个值,就直接最优化了左端点。而右端点的转移,一旦不用考虑这个左端点是不是最优的,就容易多了。

那我们定义,dpi,0dp_{i,0} 为前缀和(操作前),dpi,1dp_{i,1} 表示从 ii 及之前开始转化的最大化 Sl1+l(1l)S_{l-1}+l(1-l) 值(可以理解为操作中,但是不严谨),dpi,2dp_{i,2} 表示 ii 以及之前转化结束,所能达到的最大和(操作后)。

之所以定义三个状态,而不是两个,是为了避免重复操作。

dpi,0=dpi1,0+aidpi,1=max(dpi1,1,dpi1,0+i(1i))dpi,2=max(dpi1,2+ai,dpi,1+i(i+1))dp_{i,0}=dp_{i-1,0}+a_i\\ dp_{i,1}=\max(dp_{i-1,1},dp_{i-1,0}+i(1-i))\\ dp_{i,2}=\max(dp_{i-1,2}+a_i,dp_{i,1}+i(i+1))

这个状态转移式子中 dpi,2dp_{i,2} 之所以有两个来源,就是其状态定义,在 ii 之前转化结束一个来源,在 ii 结束一个来源。

ii 定义为这个长整型,小心溢出

1
2
3
4
5
6
7
8
9
// dp[i][0] 就是前缀和,dp[i][1]是这个预备变量
for (ll i=1;i<=N;++i) {
dp[i][0]=dp[i-1][0]+A[i];
// dp[i][1] 代表从 i 开始变换,我们能得到的最大值(左端)
// 即 S[i-1]+i*(1-i) 的最大值
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+i*(1-i));
dp[i][2]=max(dp[i-1][2]+A[i],dp[i][1]+i*(i+1));
}
cout<<max(dp[N][0],dp[N][2])<<"\n";

AC代码

AC
https://codeforces.com/contest/2169/submission/360407408

心路历程(WA,TLE,MLE……)