炼石计划 NOIP 模拟赛 #20
A.
\(kx + (\sum_{i=1}^{k} a_i - 1) \times y = k(x - y) + y \times \sum_{i=1}^{k} a_i\)
\((a_1 - 1) * 1 + (a_2 - 1) * (a_1 - 1) * 1 + (a_3 - 1) * (a_2 - 1) * (a_1 - 1) * 1\)
$ \prod_{i=1}^{k} a_i > N$
两数和相等时乘积最大,因此 \(a\) 数组中任意两个数的差的绝对值小于等于1(一定是形如 \(p, p + 1\))。
设有 \(t\) 个 \(p\)。
\(p^t \times (p + 1) ^ {k-t} > N\)
\(k(x - y) + y \times (t \times p + (k - t) \times (p + 1)) = k(x-y) + y \times (k \times p + k - t)\)
变成了满足一式的情况下要求二式最小。
枚举 \(k\) 是 \(\log N\),可以再枚举一个 \(t\),再对 \(p\) 二分。
时间复杂度 \(\log ^4 N\)。
代码
#include<bits/stdc++.h>
#define int __int128
using namespace std;
const int N = 1e6 + 7;
const int M = 61;
const int inf = 1e20;
void write(int x)
{
if(x<0)
putchar('-'),x=-x;
if(x>9)
write(x/10);
putchar(x%10+'0');
return;
}
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
signed main() {
freopen("dice.in", "r", stdin);
freopen("dice.out", "w", stdout);
int N, x, y;
N = read(), x = read(), y = read();
int ans = inf;
for(int k = 1; k <= M; k ++) {
int l = 0, r = inf;
for(int t = 1; t <= k; t ++) {
int l = 0, r = inf;
int res;
while(l <= r) {
int mid = l + r >> 1;
int flag;
int now = 1;
int ck = 1;
for(int i = 1; i <= t; i ++) {
now = now * mid;
if(now > N) {
ck = 0;
break;
}
}
if(!ck) flag = 1;
if(ck) {
for(int i = 1; i <= k - t; i ++) {
now = now * (mid + 1);
if(now > N) {
ck = 0;
break;
}
}
}
if(!ck) flag = 1;
if(ck && now > N) flag = 1;
else if(ck) flag = 0;
if(flag) {
res = mid;
r = mid - 1;
}
else l = mid + 1;
}
// cout << k * (x - y) + y * (k * res + k - t) << endl;
ans = min(ans, (k * (x - y) + y * (k * res + k - t)));
}
}
write(ans);
}
B.
好题。
设 \(L_i (j)\) 表示序列中等于 \(i\) 的第 \(j\) 个出现的位置。我们要找最小的 \(k\),使得 \(L_k(j) - L_k(j-1) > x\),记 \(f_i\) 表示 \(L_i(j) - L_i(j-1)\) 的最大值, \(G(i)\) 表示 \(\max_{j=1}^{i}f(j)\),要求的就是最小的 \(k\) 使得 \(G(k) > x\)。
对于交换 \(a_p, a_{p+1}\),找到 \(x, y\) 满足 \(L_{a[p]}(x) = p, L_{a_[p+1]}(y) = p + 1\), \(L_{a[p]}[x] ++,L_{a[p + 1]}[y] --\)。然后用线段树维护 \(G\),每次查询二分即可,时间复杂度 \(O(q\log^2n)\)
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
int a[N];
struct tree {
int l, r;
int mxx;
}T[N << 2];
void build(int l, int r, int rt = 1) {
T[rt].l = l, T[rt].r = r;
if(l == r) {
return;
}
int mid = l + r >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
T[rt].mxx = max(T[rt << 1].mxx, T[rt << 1 | 1].mxx);
}
void modify(int pos, int x, int rt = 1) {
if(T[rt].l == T[rt].r) {
T[rt].mxx = x;
return;
}
int mid = T[rt].l + T[rt].r >> 1;
if(pos <= mid) modify(pos, x, rt << 1);
else modify(pos, x, rt << 1 | 1);
T[rt].mxx = max(T[rt << 1].mxx, T[rt << 1 | 1].mxx);
}
int query(int l, int r, int rt = 1) {
if(l <= T[rt].l && r >= T[rt].r) {
return T[rt].mxx;
}
int mid = T[rt].l + T[rt].r >> 1;
int ans = -1;
if(l <= mid) ans = max(ans, query(l, r, rt<< 1));
if(r > mid) ans = max(ans, query(l, r, rt << 1 | 1));
return ans;
}
set<int> L[N];
multiset<int> dis[N];
void ins(int x) {
auto it = L[a[x]].lower_bound(x);
int r = *it, l = *(--it);
L[a[x]].insert(x);
dis[a[x]].erase(dis[a[x]].lower_bound(r - l - 1));
dis[a[x]].insert(r - x - 1);
dis[a[x]].insert(x - l - 1);
modify(a[x] + 1, *dis[a[x]].rbegin());
}
void del(int x) {
L[a[x]].erase(x);
auto it = L[a[x]].lower_bound(x);
int r = *it, l = *(--it);
dis[a[x]].insert(r - l - 1);
dis[a[x]].erase(dis[a[x]].lower_bound(r - x - 1));
dis[a[x]].erase(dis[a[x]].lower_bound(x - l - 1));
modify(a[x] + 1, *dis[a[x]].rbegin());
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
freopen("star.in", "r", stdin);
freopen("star.out", "w", stdout);
int n, q;
cin >> n >> q;
build(1, n + 1);
for(int i = 0; i < n; i ++) {
L[i].insert(0), L[i].insert(n + 1);
dis[i].insert(n + 1);
modify(i + 1, n + 1);
}
for(int i = 1; i <= n; i ++) {
cin >> a[i];
ins(i);
}
while(q --) {
int x, y;
cin >> x >> y;
if(x == 1) {
del(y);
del(y + 1);
swap(a[y], a[y + 1]);
ins(y);
ins(y + 1);
}
else {
int l = 1, r = n;
int ans = n + 1;
while(l <= r) {
int mid = l + r >> 1;
if(query(1, mid) >= y) {
ans = mid;
r = mid - 1;
}
else l = mid + 1;
}
cout << ans - 1 << endl;
}
}
}