[JOISC2017]门票安排
作者:互联网
[JOISC2017]门票安排
题面
题解
首先考虑\(c[i]=1\)的情况,首先默认所有\(l\leq r\),而且一开始所有人选择的均为\([l,r]\),然后我们考虑把一些区间反转过来。
然后有一条比较显然的性质,就是对于两个不交的区间,在最优解中一定不会同时把他们两个反转过来,否则只有可能变多,而不会变少。
那么假设所有可能反转区间的交集为\([x,y]\),一开始时所有门票被覆盖次数是\(a_i\),反转后是\(b_i\),在\([x,y]\)中\(b\)的最大值位置为\(t\),那么又有三条性质:
性质1:\(b_t\geq \max b_i-1\)
证明1:如果有\(b_t\leq \max b_i-2\),那么可以取消反转一个\(l=x\)的区间和\(r=y\)的区间,答案不会更劣,同时满足了这条性质。
性质2:\(a_t\geq \max a_i\)
证明2:如果存在\(a_k>a_t\)的话,则\(k\notin [x,y]\),那么不是所有的区间都会覆盖\(k\),而\(t\)必然被所有反转区间覆盖,又因为有上一条性质,所以\(k,t\)满足:
\(a_k-a_t\geq 1,b_t-b_k\geq -1 \Rightarrow a_k-a_t+b_t-b_k\geq 0\Leftrightarrow a_k-b_k\geq a_t-b_t\)
这个式子的意义就是\(k\)上反转的区间个数大于等于\(t\),这与上面所述的矛盾。
性质3:最优解的\(t\)满足\(a_t=\max a_i\)且\(t\)尽量小或大。
证明3:所有反转区间都会经过最小或最大点。
最后考虑我们的算法:
首先拿出一个使得\(a_t\)最大的最小或最大的\(t\),然后再二分答案\(\text{mid}\),那么反转后该点的值为\(\text{mid}\)或\(\text{mid}-1\),可以分别算出来我总共进行的反转次数\(\text{cnt}\)再分开检验。
把所有跨过\(t\)的区间拿出来再从左到右扫描线,因为后面的反转对前面的操作有影响,但是现在我们知道了操作数,所以我们可以的出这个点最少要被反多少下。反转的话维护一个大根堆,每次反转找出右端点最靠有的区间反肯定最右,因为对后面的位置影响小。\([t+1,n]\)可以由前面的操作差分判断,具体细节详见代码。
代码
#include <bits/stdc++.h>
using namespace std;
int gi() {
int res = 0, w = 1;
char ch = getchar();
while (ch != '-' && !isdigit(ch)) ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) res = res * 10 + ch - '0', ch = getchar();
return res * w;
}
typedef long long LL;
const int MAX_N = 2e5 + 5;
int N, M;
int A[MAX_N], B[MAX_N];
LL C[MAX_N], c[MAX_N], tag[MAX_N];
#define fi first
#define se second
vector<pair<int, LL> > pot[MAX_N];
priority_queue<pair<int, LL> > Q;
bool check(LL mid, int t, LL cnt) {
if (mid < cnt) return 0;
for (int i = 1; i <= N; i++) pot[i].clear();
while (!Q.empty()) Q.pop();
for (int i = 1; i <= N; i++) tag[i] = 0;
for (int i = 1; i <= M; i++)
if (A[i] <= t && t < B[i]) pot[A[i]].push_back(make_pair(B[i], C[i]));
LL now = 0;
for (int i = 1; i <= t; i++) {
for (auto j : pot[i]) Q.push(j);
while (c[i] - now + cnt > mid) {
if (Q.empty()) return 0;
auto p = Q.top(); Q.pop();
LL need = min(p.se, (c[i] - now + cnt - mid + 1) >> 1);
now += need, cnt -= need, tag[p.fi] += need << 1;
if (p.se != need) Q.push({p.fi, p.se - need});
}
}
tag[t + 1] -= now;
for (int i = t + 1; i <= N; i++) {
tag[i] += tag[i - 1];
if (tag[i] + c[i] > mid) return 0;
}
return 1;
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
N = gi(), M = gi();
for (int i = 1; i <= M; i++) {
A[i] = gi(), B[i] = gi(), C[i] = gi();
if (A[i] > B[i]) swap(A[i], B[i]);
c[A[i]] += C[i], c[B[i]] -= C[i];
}
for (int i = 1; i <= N; i++) c[i] += c[i - 1];
int mxl = 0, mxr = 0;
for (int i = 1; i <= N; i++) if (c[i] > c[mxl]) mxl = i;
for (int i = N; i >= 1; i--) if (c[i] > c[mxr]) mxr = i;
LL l = 0, r = c[mxl], ans = r;
while (l <= r) {
LL mid = (l + r) >> 1;
if (check(mid, mxl, c[mxl] - mid) || check(mid, mxl, c[mxl] - mid + 1) ||
check(mid, mxr, c[mxr] - mid) || check(mid, mxr, c[mxr] - mid + 1)) ans = mid, r = mid - 1;
else l = mid + 1;
}
printf("%lld\n", ans);
return 0;
}
标签:JOISC2017,int,反转,安排,mid,门票,ch,mxl,MAX 来源: https://www.cnblogs.com/heyujun/p/13722367.html