P8924 「GMOI R1-T1」Perfect Math Class
思路
- 因为 n n n 和 m m m 均不超过 100 100 100,所以我们用 v i s [ i ] [ j ] vis[i][j] vis[i][j] 表示第 i i i 行第 j j j 列是否函数上。当然,用 map 去维护点的信息也可以。
- 用因为 k ≤ 7 k\le7 k≤7,故计算函数值 y y y 的时候要开 long long。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxk = 15;
map <pair <int, int>, int> vis;
int n, m, k, a[maxk];
int main ()
{
scanf ("%d %d %d", &n, &m, &k);
for (int i = 1; i <= k + 1; i++)
scanf ("%d", &a[i - 1]);
for (int x = 0; x <= n - 1; x ++)
{
long long y = 0, p = 1;
for (int i = 1; i <= k + 1; i++)
{
y += p * a[i - 1];
p *= x;
}
if (y < 0 || y > m - 1) continue;
vis.insert (make_pair (make_pair (x, y), 1));
}
for (int i = m - 1; i >= 0; i--)
{
for (int j = 0; j <= n - 1; j++)
if (vis.find (make_pair (j, i)) == vis.end ())
printf (".");
else printf ("*");
printf ("\n");
}
return 0;
}
P8925 「GMOI R1-T2」Light
思路
- 我们设左边的第 n n n 项是 a n a_n an,右边第 n n n 项是 b n b_n bn。易得, a 1 = − 2 L a_1=-2L a1=−2L, b 1 = 2 R b_1=2R b1=2R。
- 根据题意, a n + b n − 1 2 = − L \frac{a_n+b_{n-1}}{2}=-L 2an+bn−1=−L 且 b n + a n − 1 2 = R \frac{b_n+a_{n-1}}{2}=R 2bn+an−1=R。故易得, a n − a n − 2 = − 2 R − 2 L a_n-a_{n-2}=-2R-2L an−an−2=−2R−2L。分奇偶讨论,当 n n n 为奇数时, a n = − ( n + 1 ) L − ( n − 1 ) R a_n=-(n+1)L-(n-1)R an=−(n+1)L−(n−1)R;当 n n n 为偶数时, a n = − n ( R + L ) a_n=-n(R+L) an=−n(R+L)。 b n b_n bn 同理可得。
代码
#include <bits/stdc++.h>
using namespace std;
long long l, r, n, ans;
int T;
int main ()
{
scanf ("%d", &T);
scanf ("%lld %lld", &l, &r);
while (T --)
{
char turn;
cin >> turn, scanf ("%lld", &n);
if (turn == 'L')
{
if (n & 1ll) ans = -(n + 1) * l - (n - 1) * r;
else ans = -n * l - n * r;
}
else
{
if (n & 1ll) ans = (n + 1) * r + (n - 1) * l;
else ans = n * r + n * l;
}
printf ("%lld\n", ans);
}
return 0;
}
P8926 「GMOI R1-T3」Number Pair
思路
- 设 x = gcd ( x , y ) x 1 x=\gcd(x,y)x_1 x=gcd(x,y)x1 与 y = gcd ( x , y ) y 1 y=\gcd(x,y)y_1 y=gcd(x,y)y1,则易得 gcd ( x 1 , y 1 ) = 1 \gcd(x_1,y_1)=1 gcd(x1,y1)=1。由 k gcd ( x , y ) = lcm ( x , y ) k\gcd(x,y)=\operatorname{lcm}(x,y) kgcd(x,y)=lcm(x,y) 得, k gcd ( x , y ) 2 = x y k{\gcd(x,y)}^2=xy kgcd(x,y)2=xy,进而 k = x 1 y 1 k=x_1y_1 k=x1y1,故欲求满足条件的 ( x , y ) (x,y) (x,y) 的对数,我们需要找到 k k k 分解成两个互质的数相乘的方案数。
- 根据唯一分解定理把 k k k 分解成 p 1 r 1 p 2 r 2 ⋯ p n r n p_1^{r_1}p_2^{r_2}\cdots p_n^{r^n} p1r1p2r2⋯pnrn。若 p i ∣ x 1 p_i \mid x_1 pi∣x1,则 p i ∤ y 1 p_i \nmid y_1 pi∤y1。故可知, k k k 分解成两个互质的数相乘的方案数是 2 n 2^n 2n,可由快速幂得到。
- 如何得到 k k k 的标准分解式。因为 p i , i ∈ { 1 , 2 … n } p_i,i\in \{1,2\dots n\} pi,i∈{1,2…n} 中至多有一个大于 k \sqrt{k} k 的素数,所以我们可以先通过线性筛得到 1 1 1 到 1 0 8 10^8 108 内所有的素数。因为 1 1 1 到 1 0 8 10^8 108 中只有 28724216 28724216 28724216 个素数,故我们要卡着这个值开数组,避免MLE。我们将 n n n 初始化为 0 0 0,然后枚举这些素数,如果某个素数可以整除 k k k,那么 n + 1 n + 1 n+1,并不断用这个素数除 k k k 直到这个素数 ∤ k \nmid k ∤k。若退出循环后 k ≠ 1 k\ne1 k=1,则说明 k k k 的标准分解式中有一个大于 k \sqrt{k} k 的素数,那么 n + 1 n+1 n+1。
- 因为 P ≤ gcd ( x , y ) ≤ Q P\leq \gcd(x,y)\leq Q P≤gcd(x,y)≤Q,所以最后的答案就是 ( Q − P + 1 ) 2 n (Q-P+1)2^n (Q−P+1)2n。
- 但是这个题卡常了,于是我们要优化。我们发现在枚举素数进行试除的时候,若某个素数 p p p 满足了条件 p 2 > k p^2>k p2>k,则没有必要在枚举了,这时候退出循环就可以了。
- 最坏时间复杂度为 O ( 1 0 8 + 28724216 T ) \mathcal O(10^8+28724216T) O(108+28724216T)。
代码
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int maxn = 1e8 + 10;
int t, p, q, pi, zs[28724221], ans;
bool vis[maxn];
long long k;
void Init ()
{
for (int i = 2; i <= 1e8; i++)
{
if (!vis[i]) zs[++ pi] = i;
for (int j = 1; j <= pi; j++)
{
if (1ll * zs[j] * i > 1e8) break;
vis[zs[j] * i] = 1;
if (i % zs[j] == 0) break;
}
}
}
int quickpower (int b)
{
if (b == 0) return 1;
if (b == 1) return 2;
int mid = quickpower (b / 2);
if (b & 1) return 2ll * mid * mid % MOD;
else return 1ll * mid * mid % MOD;
}
int main ()
{
Init ();
scanf ("%d", &t);
while (t --)
{
scanf ("%lld %d %d", &k, &p, &q);
int n = 0;
for (int i = 1; i <= pi && k != 1; i++)
{
if (1ll * zs[i] * zs[i] > k) break;
if (k % zs[i] == 0)
{
n ++;
while (k % zs[i] == 0 && k != 1) k /= zs[i];
}
}
if (k != 1) n ++;
ans = 1ll * quickpower (n) * (q - p + 1) % MOD;
printf ("%d\n", ans);
}
return 0;
}
收获
-
第一次碰见卡空间复杂度的, 256 M B 256MB 256MB 是 268435456 268435456 268435456 字节。下图是常见数据类型的所占字节数,bool 型是 1 1 1 字节。
-
意想不到的卡常优化(思路 5)。
P8927 「GMOI R1-T4」Rain
思路
经过绝对值运算后,将会有 n n n 项符号为正, n n n 项符号为负。为了使答案尽可能地大,我们希望, p a 1 , p a 2 … p a n , q a 1 , q a 2 … q a n pa_1,pa_2\dots pa_n,qa_1,qa_2\dots qa_n pa1,pa2…pan,qa1,qa2…qan 中前 n n n 大项的符号全为正,其余较小的项符号全为负。
以 n = 2 k n=2k n=2k 和 p ≥ q p\ge q p≥q 的情况为例,我们对 a 1 , a 2 … a 2 k a_1,a_2\dots a_{2k} a1,a2…a2k 进行由大到小的排序,得到新的 a 1 , a 2 … a 2 k a_1,a_2\dots a_{2k} a1,a2…a2k。令 b 1 , 2 … 2 k b_{1,2\dots2k} b1,2…2k 为 a 1 , 2 k , 2 , 2 k − 1 , … k , k + 1 a_{1,2k,2,2k-1,\dots k,k+1} a1,2k,2,2k−1,…k,k+1。下面证明序列 b b b 满足期望。
先不考虑从 2 k 2k 2k 到 1 1 1 产生的距离,对其他距离进行讨论。当 i i i 为奇数时, ∣ b i − b i + 1 ∣ = p b i − q b i + 1 \lvert bi-b_{i+1}\rvert=pb_i-qb_{i+1} ∣bi−bi+1∣=pbi−qbi+1;当 i i i 为偶数时,由于 p b i pb_i pbi 递增且 q i + 1 q_{i+1} qi+1 递减,设 i 0 i_0 i0(如果存在的话),使得 p b i 0 ≥ q b i 0 + 1 pb_{i_0}\ge qb_{i_0+1} pbi0≥qbi0+1,则当 i < i 0 i<i_0 i<i0 时, ∣ p b i − q b i + 1 ∣ = q b i + 1 − p b i \lvert pb_i-qb_{i+1}\rvert=qb_{i+1}-pb_i ∣pbi−qbi+1∣=qbi+1−pbi;当 i ≥ i 0 i\ge i_0 i≥i0 时, ∣ p b i − q b i + 1 ∣ = p b i − q b i + 1 \lvert pb_i-qb_{i+1}\rvert=p{b_i}-qb_{i+1} ∣pbi−qbi+1∣=pbi−qbi+1。
故符号为正的
2
k
−
1
2k-1
2k−1 项为,
p
b
1
,
3
,
…
,
2
k
−
1
,
q
b
3
,
5
,
…
,
i
0
−
1
,
p
b
i
0
,
…
,
2
k
−
2
pb_{1,3,\dots, 2k-1},qb_{3,5,\dots,i_0-1},pb_{i_0,\dots, 2k-2}
pb1,3,…,2k−1,qb3,5,…,i0−1,pbi0,…,2k−2。
即,
p
a
1
,
2
,
…
,
k
,
k
+
2
,
2
k
−
i
0
2
+
1
,
q
a
2
,
3
,
…
,
i
0
2
pa_{1,2,\dots, k,k+2,2k-\frac{i_0}{2}+1},qa_{2,3,\dots,\frac{i_0}{2}}
pa1,2,…,k,k+2,2k−2i0+1,qa2,3,…,2i0。
符号为负的 2 k − 1 2k-1 2k−1 项为, p a 2 k − i 0 2 + 2 , … , 2 k , q a i 0 2 + 1 , … , 2 k pa_{2k-\frac{i_0}{2}+2,\dots,2k},qa_{\frac{i_0}{2}+1,\dots,2k} pa2k−2i0+2,…,2k,qa2i0+1,…,2k。
根据 a a a 的排序方式和 i 0 i_0 i0 的意义可知,正项中的每一个大于负项中的每一个。又 q a 1 qa_1 qa1 与 p a k + 1 pa_{k+1} pak+1 无论哪项为正,肯定大于上述的负项。故序列 b b b 符合期望。当 n n n 为奇数时,同理可得;当 p < q p<q p<q 时,同理可得,但对 a a a 的排序方式改为“小大小大……”。
时间复杂度为 O ( n log n ) \mathcal O(n\log{n}) O(nlogn)。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 100;
int n, p, q, a[maxn], cnt, b[maxn];
long long ans;
int read ()
{
int s = 0, w = 1;
char ch = getchar ();
while (ch < '0' || ch > '9')
{
if (ch == '-') w = -1;
ch = getchar ();
}
while (ch >= '0' && ch <= '9')
{
s = s * 10 + ch - '0';
ch = getchar ();
}
return s * w;
}
int main ()
{
n = read (), p = read (), q = read ();
for (int i = 1; i <= n; i++)
a[i] = read ();
sort (a + 1, a + n + 1);
for (int i = 1, j = n; i <= j; i++, j--)
{
if (i == j) b[++ cnt] = a[i];
if (p >= q)
b[++ cnt] = a[i], b[++ cnt] = a[j];
else
b[++ cnt] = a[j], b[++ cnt] = a[i];
}
for (int i = 1; i <= n; i++)
{
int to = (i == n ? 1 : i + 1);
ans += abs (1ll * p * b[i] - 1ll * q * b[to]);
}
printf ("%lld\n", ans);
for (int i = 1; i <= n; i++)
printf ("%d ", b[i]);
return 0;
}
收获
- 敢于推测,敢于证明。