比赛传送门:CF #1114。
比赛记录:点我。
又 FST 了。
【A】Got Any Grapes?
题意简述:
有三个人,第一个人需要吃绿色葡萄至少 (a) 个,第二个人需要吃绿色和紫色葡萄至少 (b) 个,第三个人需要吃绿色、紫色和黑色葡萄至少 (c) 个。
有 (x) 个绿色葡萄,(y) 个紫色葡萄,(z) 个黑色葡萄,问是否能够满足三个人的要求。
题解:
#include <cstdio>
int main() {
int x, y, z, a, b, c;
scanf("%d%d%d%d%d%d", &x, &y, &z, &a, &b, &c);
if (a < x || a + b < x + y || a + b + c < x + y + z) puts("NO");
else puts("YES");
return 0;
}
【B】Yet Another Array Partitioning Task
题意简述:
一个长度为 (n) 的数组 (a_i),要把它分成 (k) 个连续段(每段长度大于等于 (m)),每段的贡献是其中最大的 (m) 个元素,求一种分割方案使得总贡献最大。
题解:
把最大的 (mcdot k) 个数提取出来,暴力分割。
#include <cstdio>
#include <algorithm>
typedef long long LL;
int N, M, K;
int A[200005], B[200005];
LL Ans = 0;
int main(){
scanf("%d%d%d", &N, &M, &K);
for (int i = 1; i <= N; ++i) {
scanf("%d", &A[i]);
B[i] = i;
}
std::sort(B + 1, B + N + 1, [](int i, int j) { return A[i] > A[j]; });
std::sort(B + 1, B + M * K + 1);
for (int i = 1; i <= M * K; ++i) Ans += A[B[i]];
printf("%lld
", Ans);
for (int i = M; i < M * K; i += M) printf("%d ", B[i]);
puts("");
return 0;
}
【C】Trailing Loves (or L'oeufs?)
题意简述:
求 (n!)((n) 的阶乘)在 (b) 进制下末尾有几个 (0)。((1le nle 10^{18}),(2le ble 10^{12}))
题解:
相当于求最大的 (k) 使得 (b^k|n!)。记这个值为 (v_b(n))。
对于 (b=p) 是质数的情况,数论告诉我们有:(v_p(n)=sum_{i=1}^{infty}lfloorfrac{n}{p^i} floor=lfloorfrac{n}{p} floor+lfloorfrac{n}{p^2} floor+lfloorfrac{n}{p^3} floor+cdots)。
(v_p(n)) 给出了 (n!) 的标准分解式中 (p^k) 因子的次方数 (k)。
这启发我们将 (b) 质因数分解:(b=p_1^{e_1}p_2^{e_2}cdots p_k^{e_k})。
则有:
对于 (v_p(n)) 可以用这个方法计算:(v_p(n)=egin{cases}0&,n=0\lfloorfrac{n}{p} floor+v_p(lfloorfrac{n}{p} floor)&,n>0end{cases})。
#include <cstdio>
#include <algorithm>
typedef long long LL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
LL N, b;
LL p[25]; int e[25], cnt;
LL Ans = INF;
int main() {
scanf("%lld%lld", &N, &b);
for (LL i = 2; i * i <= b; ++i) if (b % i == 0) {
p[++cnt] = i, e[cnt] = 0;
while (b % i == 0) b /= i, ++e[cnt];
} if (b > 1) p[++cnt] = b, e[cnt] = 1;
for (int i = 1; i <= cnt; ++i) {
LL num = 0, m = N;
while (m) num += m / p[i], m /= p[i];
Ans = std::min(Ans, num / e[i]);
}
printf("%lld
", Ans);
return 0;
}
【D】Flood Fill
题意简述:
一个长度为 (n) 的序列 (c_1,c_2,ldots,c_n)。
你可以选择一个位置 (pos),每次操作你把 (pos) 所在联通块全部变成一个数。
问最少花费几次操作可以把整个序列变成同一个数。
一个联通块指 (c_i) 全部相同的连续一段。
题解:
先把序列 unique
一下,得到联通块个数。
令 (mathrm{f}[i][j]) 为把序列的第 (i) 位到第 (j) 位全部变成同一种颜色要花费的最小操作数。
转移:(mathrm{f}[i][j]=1+egin{cases}min(mathrm{f}[i+1][j],mathrm{f}[i][j-1])&,c[i] e c[j]\mathrm{f}[i+1][j-1]&,c[i]=c[j]end{cases})。
#include <cstdio>
#include <algorithm>
const int MN = 5005;
int N, c[MN];
int f[MN][MN];
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i)
scanf("%d", &c[i]);
N = std::unique(c + 1, c + N + 1) - c - 1;
for (int i = 1; i <= N; ++i)
f[i][i] = 0;
for (int Q = 2; Q <= N; ++Q) {
for (int i = 1; i <= N - Q + 1; ++i) {
int j = i + Q - 1;
if (c[i] == c[j]) f[i][j] = f[i + 1][j - 1] + 1;
else f[i][j] = std::min(f[i + 1][j], f[i][j - 1]) + 1;
}
}
printf("%d
", f[1][N]);
return 0;
}
【E】Arithmetic Progression
题意简述:
这是一道交互题。
交互器有一个长度为 (n)((2le nle 10^6))的等差数列 (a_1,a_2,ldots,a_n)。保证 (0le a_ile 10^9),且公差 (d=a_2-a_1>0)。
但是这个等差数列被随机打乱了,给定 (n),你需要做出不超过 (60) 次询问,得到 (a_1) 和公差 (d)。
你有两种询问:
-
? i
((1le ile n)):询问 (a_i) 的值,注意这里返回的是打乱后的。 -
> x
((0le xle 10^9)):询问这个等差数列中是否存在一个数 (a_i) 满足 (a_i>x)。
题解:
首先使用不超过 (30) 次询问 > x
,二分查找找到最大的数 (max)。
令 (d=0)。
然后使用若干次 ? i
,这里 (i) 是在 (1) 到 (n) 内等概率随机的,每次得到一个数 (a_i),令 (d=gcd(d,max-a_i))。
最后 (a_1=max-(n-1)d)。
为什么这样是正确的?显然找到最大的数的询问次数的确不超过 (30) 次。
接下来还有至少 (60-30=30) 次操作,用于更新公差 (d)。
答案正确的可能性等价于在 (0) 到 (n-1) 中等概率随机选取至少 (30) 个数,并且它们的 (gcd) 等于 (1)。
可以证明答案错误的概率小于 (1.86 imes 10^{-9})。
注意选取合适的随机数生成器,无良出题人专门卡了普通的 rand()
。
#include <cstdio>
#include <algorithm>
inline unsigned Uran() {
static unsigned x = 20021005;
return x ^= x << 17, x ^= x >> 13, x ^= x << 5;
}
inline int Mran(int L, int R) {
return Uran() % (R - L + 1) + L;
}
int N, cnt, d;
int main() {
scanf("%d", &N);
int l = N, r = 1e9, mid, x, mx = N - 1;
while (l <= r) {
mid = (l + r) >> 1;
printf("> %d
", mid - 1);
fflush(stdout);
scanf("%d", &x);
++cnt;
if (x) mx = mid, l = mid + 1;
else r = mid - 1;
}
while (cnt < 60) {
printf("? %d
", Mran(1, N));
fflush(stdout);
scanf("%d", &x);
++cnt;
d = std::__gcd(d, mx - x);
}
printf("! %d %d
", mx - (N - 1) * d, d);
return 0;
}
【F】Please, another Queries on Array?
题意简述:
一个长度为 (n) 的序列 (a_1,a_2,ldots,a_n)。有 (q) 次操作,每次操作为以下两种之一:
-
MULTIPLY l r x
:将 (a_l) 到 (a_r) 之间(包括 (a_l) 和 (a_r))的所有数乘以 (x)。 -
TOTIENT l r
:询问 (varphi(prod_{i=l}^{r}a_i)pmod{10^9+7}),其中 (varphi(x)) 表示 (x) 的欧拉函数值。
数据范围:(1le lle rle nle 4cdot 10^5),(1le qle 2cdot 10^5),(1le a_i,xle 300)。
题解:
首先想到使用线段树维护区间。
注意到 (a_i) 和 (x) 很小,不超过 (300)。
又有 (varphi(x)=xprod_{p|x}frac{p-1}{p}),这提示我们维护区间乘积以及区间质因数集合。
发现 (300) 以内的质数个数只有 (62) 个,正好在 long long
的位数范围内,这提示我们状压集合存入 long long
中。
变成了基础线段树例题,复杂度 (mathrm{O}((n+q)log^2 n))。使用原根化乘为加可以做到 (mathrm{O}((n+q)log n))。
这里给出使用原根和 BSGS 打表的代码,(10^9+7) 的原根是 (5)。
#include <cstdio>
#include <algorithm>
#include <map>
typedef unsigned long long LL;
typedef std::pair<LL, LL> pll;
const LL Mod = 1000000007, eM = 1000000006, G = 5;
const int MX = 301;
inline LL qPow(LL b, int e) {
LL a = 1;
for (; e; e >>= 1, b = b * b % Mod)
if (e & 1) a = a * b % Mod;
return a;
}
const int E[MX] = {0, 0, 381838282, 884237698, 763676564, 1, 266075974, 936544532, 145514840, 768475390, 381838283, 649233168, 647914256, 144547565, 318382808, 884237699, 527353122, 895459693, 150313666, 701153241, 763676565, 820782224, 31071444, 82047235, 29752532, 2, 526385847, 652713082, 700221090, 347375814, 266075975, 198670980, 909191404, 533470860, 277297969, 936544533, 532151948, 289190395, 82991517, 28785257, 145514841, 802264261, 202620500, 24616936, 412909726, 768475391, 463885517, 730741137, 411590814, 873089058, 381838284, 779697385, 908224129, 616590335, 34551358, 649233169, 82059366, 585390933, 729214096, 178404868, 647914257, 234516540, 580509262, 705019916, 291029680, 144547566, 915309142, 351358075, 659136251, 966284933, 318382809, 557546595, 913990230, 559624399, 671028677, 884237700, 464829799, 585777694, 410623539, 414776906, 527353123, 536950774, 184102537, 809672376, 584458782, 895459694, 406455218, 231613506, 794748008, 689565125, 150313667, 81092091, 845723799, 82908672, 112579413, 701153242, 793429096, 993378125, 254927334, 417708552, 763676566, 495914368, 161535661, 991051336, 290062405, 820782225, 998428617, 326036185, 416389640, 525353314, 31071445, 173428087, 463897648, 429750477, 967229215, 82047236, 111052372, 913022955, 560243150, 832004219, 29752533, 298466330, 616354822, 686501953, 962347544, 3, 86858192, 155622902, 672867962, 908854634, 526385848, 26958408, 297147418, 637697767, 733196357, 652713083, 40974527, 540374100, 348123209, 293399235, 700221091, 614978829, 939384877, 793780733, 295828506, 347375815, 941462681, 757326750, 52866953, 109607515, 266075976, 468364065, 846668081, 663935077, 967615976, 198670981, 792461821, 967234449, 796615188, 500828027, 909191405, 18591761, 918789056, 238291131, 565940819, 533470861, 191510652, 98758628, 966297064, 289095130, 277297970, 469628625, 788293500, 328851128, 613451788, 936544534, 176586284, 62642560, 71403401, 492996069, 532151949, 546888614, 462930373, 118754232, 227562075, 289190396, 464746954, 544692855, 494417695, 589257608, 82991518, 366164793, 175267372, 23539458, 375216401, 28785258, 636765616, 829772542, 799546834, 912024915, 145514842, 235595767, 877752650, 283920340, 543373943, 802264262, 372889612, 850522625, 671900687, 350386403, 202620501, 437098732, 380266893, 441784287, 707874467, 24616937, 798227922, 135215506, 907191596, 443862091, 412909727, 40007252, 555266369, 768794522, 845735930, 768475392, 811588759, 342799273, 349067491, 566661897, 463885518, 470015386, 492890654, 924677391, 294861231, 730741138, 942081432, 299014598, 213842495, 732754158, 411590815, 733768720, 680304612, 421188466, 998193104, 873089059, 68340229, 845700806, 344185820, 693910068, 381838285, 241119035, 468696474, 731280403, 537461184, 779697386, 54706238, 17661842, 290692910, 225734921, 908224130, 115851198, 408796690, 872301485, 678985700, 616590336, 19536043, 573802817, 115034633, 298959763, 34551359, 462560014, 422812809, 965329789, 922212382, 649233170, 729961491, 31035300, 675237517, 967146370, 82059367, 345578910, 996817111, 82704431, 321223153, 585390934, 175619009, 738808787, 677666788, 790919380, 729214097, 877615817, 323300957, 912101836, 139165026, 178404869, 434705235, 301946244, 491445797, 226594800, 647914258};
int ip[MX], v[62], pc;
LL S[MX];
void Init(int N) {
for (int i = 2; i <= N; ++i) if (!ip[i]) {
for (int j = i; j <= N; j += i) {
if (j > i) ip[j] = 1;
S[j] |= 1llu << pc;
}
v[pc++] = (i - 1) * qPow(i, Mod - 2) % Mod;
}
}
const int MS = 1 << 20 | 7;
#define li (i << 1)
#define ri (i << 1 | 1)
#define len (r - l + 1)
#define mid ((l + r) >> 1)
#define now i, l, r
#define ls li, l, mid
#define rs ri, mid + 1, r
#define Root 1, 1, N
#define St int i, int l, int r
LL sum[MS], st[MS], t1[MS], t2[MS];
inline void P(St, LL v, LL s) {
sum[i] = (sum[i] + len * v) % eM, st[i] |= s;
t1[i] = (t1[i] + v) % eM, t2[i] |= s;
}
inline void Pd(St) {
if (t1[i] || t2[i]) {
P(ls, t1[i], t2[i]), P(rs, t1[i], t2[i]);
t1[i] = t2[i] = 0;
}
}
void Mdf(St, int a, int b, LL v, LL s) {
if (r < a || b < l) return ;
if (a <= l && r <= b) return P(now, v, s);
Pd(now);
Mdf(ls, a, b, v, s), Mdf(rs, a, b, v, s);
sum[i] = (sum[li] + sum[ri]) % eM;
st[i] = st[li] | st[ri];
}
pll Qur(St, int a, int b) {
if (r < a || b < l) return pll(0llu, 0llu);
if (a <= l && r <= b) return pll(sum[i], st[i]);
Pd(now);
pll p1 = Qur(ls, a, b), p2 = Qur(rs, a, b);
return pll((p1.first + p2.first) % eM, p1.second | p2.second);
}
int N, Q;
int main() {
int x;
Init(300);
scanf("%d%d", &N, &Q);
for (int i = 1; i <= N; ++i) {
scanf("%d", &x);
Mdf(Root, i, i, E[x], S[x]);
}
for (int q = 1; q <= Q; ++q) {
char opt[9]; int l, r;
scanf("%s%d%d", opt, &l, &r);
if (*opt == 'T') {
pll P = Qur(Root, l, r);
LL Ans = qPow(G, P.first);
for (int i = 0; i < pc; ++i)
if (P.second >> i & 1) Ans = Ans * v[i] % Mod;
printf("%llu
", Ans);
}
else scanf("%d", &x), Mdf(Root, l, r, E[x], S[x]);
}
return 0;
}