Maximum Sine

题意

求 $[a, b]$ 中使得 $f(x) = \left|\sin\left(\frac{p}{q}\pi x\right)\right|$ 取得最大值的整数 $x$。且 $x$ 可能有多个取值,请输出符合条件的最小的 $x$。

$t$ 组询问,每次给出 $a,b,p,q$。

数据范围满足 $1 \le t \le 100,0 \le a \le b \le 10^9,1 \le p,q \le 10^9$

单独为本题开一篇文章,理由在于其具有复杂度优秀的三种解法,复杂度逐渐变低而效率逐渐提高。

扩展做法将专注于解决从本题中抽象出的模型。

于是先放一个最好理解的本题做法,作为讲本题时的内容

题解

观察容易得出要求一个小于线性的做法。

考虑如何转化本题的设问。

要求使 $f(x) =\left|\sin\left(\frac{p}{q}\pi x\right)\right|$ 最大的整数 $x$,考虑三角函数的周期性,我们有如下转化。

要求上式 $\sin$ 的绝对值最大,即要求 $\sin$ 括号内的值 $\frac{p}{q}\pi x $ 最接近于 $(k + \frac{1}{2}) \pi ,k \in Z$。

于是就是 $\frac{px}{q}$ 最接近于 $k + \frac12$

也就是 $\frac{px}{q}$ 的小数部分最接近于 $\frac12$

发现带着小数不舒服,考虑变成整数操作,发现可以直接同乘 $2q$

就变成了 $2px \bmod 2q$ 最接近于 $q$(这个取模可以参考模运算下的乘法,把取小数看成对 $1$ 取模来理解)。

于是这题最难的转化就结束了。

剩下的部分类似于 BSGS 的过程,具体而言,把 $[l,r]$ (即题中的 $[a,b]$)分成长度为根号级别的根号块,首先处理出每个块块首的元素的 $2px \bmod 2q$ 的值,记录并排序。

然后的操作就是每次将每个块内向后移动一个位置,即变成 $2p(x+1) \bmod 2q$。

于是每次操作相当于把我们排序后的数组都加上 $2p$ 这个定值,显然对于每个数都加上再找到最接近 $q$ 的值有很多冗余操作,因为很多位置不可能对答案产生贡献。

考虑如何减少冗余操作,我们发现每次操作是整体加上 $2p$ 再对 $2q$ 取模,相当于把最后几项旋转到最前边。

而这样做代码实现不很好写,考虑加上这些 $2p$ 之后取模要找与 $q$ 最接近的数,我们可以直接把 $q$ 减去这些 $2p$ 然后取模,在原数列中二分找到可能产生贡献的那个位置,把前后两个位置都考虑下取个 $\min$ 即可。

有一些边界需要注意,这样的做法是 $O(\sqrt{n} \log n)$ 的,因为分块后有 $\sqrt{n}$ 次移动且每次二分的复杂度是 $\log n$。

作为对不易描述的细节的说明,放一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include<cstdio>
#include<cmath>
#include<algorithm>
namespace Pozhu{
using namespace std;
typedef long long ll;
#define N 100010
ll l,r,p,q;
struct node{
ll v,id;
node(ll _v = 0,ll _id = 0) : v(_v),id(_id){}
friend bool operator < (node a,node b)
{return a.v != b.v ? a.v < b.v : a.id < b.id;}
friend bool operator == (node a,node b)
{return a.v == b.v;} // if you set the id there,you will cxk.
}a[N];

void main()
{
scanf("%lld%lld%lld%lld",&l,&r,&p,&q);
int siz = sqrt(r - l + 1);
int tot = 0;
ll mod = q + q;
ll maxx = 0x3f3f3f3f3f3f3f3f,mxpos;
for(ll i = l;i <= r;i += siz)
{
if(i + siz - 1 <= r) a[++tot] = node(2 * p * i % mod,i);
else
{
for(int j = i;j <= r;j++)
{
ll val = abs(2 * p * j % mod - q);
if(val < maxx)
maxx = val,mxpos = j;
}
}
}
sort(a+1,a+tot+1);
tot = unique(a+1,a+tot+1)-a-1;
for(ll i = 0,tmp = 0;i < siz;i++,tmp = (tmp + 2 * p) % mod)
{
ll nval = (q - tmp + mod) % mod;
int pos = lower_bound(a+1,a+tot+1,node(nval,-1)) - a;
if(pos <= tot)
{
if((a[pos].v - nval) % mod < maxx || ((a[pos].v - nval) % mod == maxx && a[pos].id + i < mxpos))
maxx = (a[pos].v - nval) % mod,mxpos = a[pos].id + i;
}
else
{
if((mod - (nval - a[1].v)) % mod < maxx || ((mod - (nval - a[1].v)) % mod == maxx && a[1].id + i < mxpos))
maxx = (mod - (nval - a[1].v)) % mod,mxpos = a[1].id + i;
}
pos--;
if(pos >= 1)
{
if((nval - a[pos].v) % mod < maxx || ((nval - a[pos].v) % mod == maxx && a[pos].id + i < mxpos))
maxx = (nval - a[pos].v) % mod,mxpos = a[pos].id + i;
}
else
{
if((mod - (a[tot].v - nval)) % mod < maxx || ((mod - (a[tot].v - nval)) % mod == maxx && a[tot].id + i < mxpos))
maxx = (mod - (a[tot].v - nval)) % mod,mxpos = a[tot].id + i;
}
}
printf("%lld\n",mxpos);
return;
}

}

signed main()
{
int T;
for(scanf("%d",&T);T;T--)
Pozhu :: main();
return 0;
}

于是原题题解的部分结束了。

下面的内容(如果我写了的话)将作为对于这题的延申。

扩展

我们从上边那题中抽象出的问题为:

求这个式子的最大值。