给定n个点,每个点权值为a[i],定义u到v的无向边的边权为min(a[u],a[u+1],…,a[v])(u
问通过k次修改后,能使该无向图的直径最大。
定义图的直径为max(dis[u][v]) 1<=u < v<=n,dis[u][v]表示u到v的最短路径。
利用上述结论3。对于判断是否可以达到答案值res
#include
using namespace std;
#define ll long long
const int maxn = 200010;
int n, k;
ll a[maxn];
vector<pair<int, ll> > poses;
void pop_back() {
for (auto p: poses) {
a[p.first] = p.second;
}
}
bool check(ll val) {
poses.clear();
int count = 0;
ll mx = 1, mn = 1000000000;
for (int i = 1; i <= n; ++i) {
if (a[i] * 2 < val) {
poses.push_back({i, a[i]});
a[i] = 1000000000;
++count;
}
mn = min(mn, a[i]);
mx = max(mx, a[i]);
}
count = k - count;
if (count < 0) {
pop_back();
return false;
} else if (count > 1) {
pop_back();
return true;
} else if (count == 1) {
pop_back();
return mx >= val;
}
// count == 0
ll res = min(a[1], a[2]);
for (int i = 2; i + 1 <= n; ++i) {
res = max(res, min(a[i], a[i+1]));
}
res = min(res, 2 * mn);
pop_back();
return res >= val;
}
void solve() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i]);
}
ll l = 1, r = 1000000000, mid;
while (l < r) {
mid = (l + r + 1) / 2;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
printf("%d\n", l);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
}
利用结论3,我们可以将k-1次操作应用到权值最小的前k-1个节点上,再把剩余的一次操作,替换到每个节点,计算每个情况的直径,取所有情况的最大值。
证明:
当k为1时,用上述策略,显然成立。
当k大于1时,因为答案为min(max(dis[i][i+1]), 2min(a[1],…,a[n])),我们尽力去最大化max(dis[i][i+1])和2min(a[1],…,a[n])。对于max(dis[i][i+1]),我们只需要花费最多两次操作,即可让他变成1000000000(修改相邻的两个节点);因此,剩余的次数,我们可以用来最大化min(a[1],…,a[n])。
替换k-1个节点后,要遍历替换每个节点的情况,我们可以用前缀、后缀思想做优化,详见代码。
#include
using namespace std;
#define ll long long
const int maxn = 200010;
int n, k;
struct node {
ll val;
int pos;
}a[maxn];
bool cmp1(const node &a, const node &b) {
return a.val < b.val;
}
bool cmp2(const node &a, const node &b) {
return a.pos < b.pos;
}
ll mn_pre[maxn], mn_suf[maxn];
ll pre[maxn], suf[maxn];
void debug() {
printf("a:\n");
for (int i = 1; i <= n; ++i) {
printf("%lld ", a[i].val);
}
printf("\n-------------------------\n");
printf("mn_pre:\n");
for (int i = 1; i <= n; ++i) {
printf("%lld ", mn_pre[i]);
}
printf("\n-------------------------\n");
printf("mn_suf:\n");
for (int i = 1; i <= n; ++i) {
printf("%lld ", mn_suf[i]);
}
printf("\n-------------------------\n");
printf("pre:\n");
for (int i = 1; i <= n; ++i) {
printf("%lld ", pre[i]);
}
printf("\n-------------------------\n");
printf("suf:\n");
for (int i = 1; i <= n; ++i) {
printf("%lld ", suf[i]);
}
printf("\n-------------------------\n");
}
void solve() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) {
scanf("%lld", &a[i].val);
a[i].pos = i;
}
sort(a + 1, a + n + 1, cmp1);
for (int i = 1; i < k; ++i) {
a[i].val = 1000000000;
}
sort(a + 1, a + n + 1, cmp2);
mn_pre[1] = a[1].val;
for (int i = 2; i <= n; ++i) {
mn_pre[i] = min(mn_pre[i-1], a[i].val);
}
mn_suf[n] = a[n].val;
for (int i = n - 1; i >= 1; --i) {
mn_suf[i] = min(mn_suf[i+1], a[i].val);
}
pre[1] = 0;
pre[2] = min(a[1].val, a[2].val);
for (int i = 3; i <= n; ++i) {
pre[i] = max(min(a[i].val, a[i-1].val), pre[i-1]);
}
suf[n] = 0;
suf[n-1] = min(a[n-1].val, a[n].val);
for (int i = n - 2; i >= 1; --i) {
suf[i] = max(min(a[i].val, a[i+1].val), suf[i+1]);
}
// debug();
ll res = 0;
for (int i = 1; i <= n; ++i) {
ll last = a[i].val;
a[i].val = 1000000000;
ll tmp = 0;// max(a[i], a[i+1])
if (i > 1) {
tmp = max(tmp, min(a[i-1].val, a[i].val));
}
if (i < n) {
tmp = max(tmp, min(a[i].val, a[i+1].val));
}
if (i > 2) {
tmp = max(tmp, pre[i-1]);
}
if (i < n - 1) {
tmp = max(tmp, suf[i+1]);
}
ll mn = a[i].val;// min(a[1]...a[n])
if (i > 1) {
mn = min(mn, mn_pre[i-1]);
}
if (i < n) {
mn = min(mn, mn_suf[i+1]);
}
// printf("%d: {%lld, %lld}\n", i, tmp, mn);
res = max(res, min(tmp, 2 * mn));
a[i].val = last;
}
printf("%lld\n", res);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
}
如果觉得文章不错的话,weixin gongzhonghao 搜索下 对方正在debug,一起快乐刷题吧~