4.11省选模拟
4.11省选模拟
鸽子来更博客了。昨天太累的就咕了,什么时候有时间再补上吧。。
T1 分头行动
我的 dp 状态有点奇怪,导致根本没有优化的空间,相当于是把自己送进了坟墓。。
首先一个很显然的性质,最后的序列一定是一个是前缀的最大值,另一个是不确定的。
那么我们由 \(f(i,j)\) 表示前 \(i\) 个物品,一个序列的最后一项是 \(j\),另一个序列的最后一项是 \(s[i]\),即前缀最大值。
那么结合上面的性质,我们的转移可以分成下面几种。
-
\(a[i]>s[i-1]\) 那么肯定是放在另一个序列里面的,所以 \(f[i][j] +=a[i]\)
-
\(a[i] \le s[i-1]\) 又分为几种子情况
1.\(a[i]\le j\),那么其实相当于一个区间加等差数列 \(f[i][j] += j\)
2.\(a[i] \ge j\),\(f[i][a[i]]=\min(f[i-1][j]+a[i])\),其实相当于一个区间取反
\(f[i][j]+=s[i-1]\),这个相当于是选另外的那个序列
然后我们接着进行观察这个东西,发现其实就是要求你进行区间加等差数列,区间取 min,区间加和。那么区间加等差数列的其实可以抽象成一条直线,然后维护这个凸包。利用分块接着进行维护即可。
//editor : DRYAYST
//Wo shi ge da SHA BI
#include<bits/stdc++.h>
#define g() getchar()
#define il inline
#define ull unsigned long long
#define eps 1e-10
#define ll long long
#define pa pair<int, int>
#define for_1(i, n) for(int i = 1; i <= (n); ++i)
#define for_0(i, n) for(int i = 0; i < (n); ++i)
#define for_xy(i, x, y) for(int i = (x); i <= (y); ++i)
#define for_yx(i, y, x) for(int i = (y); i >= (x); --i)
#define for_edge(i, x) for(int i = head[x]; i; i = nxt[i])
#define int long long
#define DB double
#define ls (p<<1)
#define rs (p<<1|1)
#define m_p make_pair
#define fi first
#define se second
using namespace std;
const int N = 71000, INF = 1e18, mod = 1e9 + 7;
il int qpow(int x, int k) {int ans = 1; while(k) {if(k & 1) ans = ans * x % mod; x = x * x % mod; k >>= 1; } return ans; }
il int Add(int x, int y) {return (x += y) %= mod;}
il int Del(int x, int y) {return (x = x - y + mod) % mod;}
il int Mul(int x, int y) {return x * y % mod;}
il int inv(int x) {return qpow(x, mod - 2); }
inline int re() {
int x = 0, p = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * p;
}
const int B = 300, M = N/B;
int n, top;
int a[100000], f[100000], sta[10000][B], Min[10000];
int ad1[100000], ad2[100000], s[100000];
il DB Calck(int x, int y) {return ((f[x] - f[y]) * 1.0) / ((x - y) * 1.0);}
il void TZ(int x) {while(Min[x] > 1 and f[sta[x][Min[x]]] + sta[x][Min[x]] * ad2[x] >= f[sta[x][Min[x] - 1]] + sta[x][Min[x] - 1] * ad2[x]) Min[x]--; }
il void Build(int id) {//OK,对于权值分块,构建关于等差数列的凸包,最小值,所以是下凸壳
int l = id * B, r = B * (id + 1) - 1, top = 0;
for(int i = l; i <= r; ++i) {
if(top > 0 and f[i] >= f[sta[id][top]]) continue;
while(top > 1 and Calck(i, sta[id][top]) <= Calck(sta[id][top], sta[id][top - 1])) top--;
sta[id][++top] = i;
}
Min[id] = top;
}
signed main() {
freopen("separate.in","r",stdin); freopen("separate.out","w",stdout);
n = re(); for_1(i, n) a[i] = re();
memset(f, 0x3f, sizeof(f)); f[0] =0;
// f[0] = 0;
for(int i = 0; i <= n / B; ++i) Build(i);
for(int i = 1; i <= n; ++i) s[i] = max(s[i-1], a[i]);
for(int i = 1; i <= n; ++i) {
if(a[i] > s[i-1]) for(int j = 0; j <= n / B; ++j) ad1[j] += a[i];
else {
int id = a[i] / B; int l = id * B, r = (id + 1) * B - 1;
for(int j = l; j <= r; ++j) f[j] += ad1[id] + ad2[id] * j; int mi = INF;
ad1[id] = ad2[id] = 0;
for(int j = 0; j < id; ++j) mi = min(mi, f[sta[j][Min[j]]] + ad1[j] + ad2[j] * sta[j][Min[j]]);
for(int j = l; j <= a[i]; ++j) mi = min(mi, f[j]);
for(int j = a[i]; j <= r; ++j) f[j] += j;f[a[i]] = min(f[a[i]], mi + a[i]);
for(int j = id + 1; j <= n/B; ++j) ad2[j]++, TZ(j);
for(int j = l; j < a[i]; ++j) f[j] += s[i-1];
for(int j = 0; j < id; ++j) ad1[j] += s[i-1];
Build(id);
}
}
int ans = INF;
for(int i = 0; i <= n; ++i) ans = min(ans, f[i] + ad1[i/B] + ad2[i/B] * i);
cout<<ans<<endl;
}
/*
19
15 19 9 16 3 15 5 4 6 8 8 15 10 2 14 16 11 14 17
*/
T2 乱数假文
这个东西比较的神奇。好了下一题。
T3 你的世界
是个题单里面的原题。但是我没做诶。
首先可以观察到的是这个每次配对的贡献如果看成一个函数的话,那么就是一个下凸的函数。因为一开始选择的点肯定是比较小的,后面的选择是越来越大的。因此这个函数具有凸性。
然后我们可以看到的是要求选 k 次,然后发现这个函数还是个凸性的,因此想到 wqs 二分进行判断。那么剩下的就是类似于一个二分图匹配的过程了,但是很明显这个复杂度是肯定过不去的。所以考虑模拟费用流,每次先选择最小的那个 a 和 b 进行配对,然后利用反悔贪心,把负的权值加进堆里面,这样就做完了。
//editor : DRYAYST
//Wo shi ge da SHA BI
#include<bits/stdc++.h>
#define g() getchar()
#define il inline
#define ull unsigned long long
#define eps 1e-10
#define ll long long
#define pa pair<int, int>
#define for_1(i, n) for(int i = 1; i <= (n); ++i)
#define for_0(i, n) for(int i = 0; i < (n); ++i)
#define for_xy(i, x, y) for(int i = (x); i <= (y); ++i)
#define for_yx(i, y, x) for(int i = (y); i >= (x); --i)
#define for_edge(i, x) for(int i = head[x]; i; i = nxt[i])
#define int long long
#define DB double
#define ls (p<<1)
#define rs (p<<1|1)
#define m_p make_pair
#define fi first
#define se second
using namespace std;
const int N = 1e6 + 10, INF = 0x7f7f7f7f, mod = 1e9 + 7;
il int qpow(int x, int k) {int ans = 1; while(k) {if(k & 1) ans = ans * x % mod; x = x * x % mod; k >>= 1; } return ans; }
il int Add(int x, int y) {return (x += y) %= mod;}
il int Del(int x, int y) {return (x = x - y + mod) % mod;}
il int Mul(int x, int y) {return x * y % mod;}
il int inv(int x) {return qpow(x, mod - 2); }
inline int re() {
int x = 0, p = 1;
char ch = getchar();
while(ch > '9' || ch < '0') {if(ch == '-') p = -1; ch = getchar();}
while(ch <= '9' and ch >= '0') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
return x * p;
}
int n, k, ans, sum, cnt;
int a[N], b[N];
priority_queue<pa, vector<pa>, greater<pa> > q;
il bool check(int x) {
while(q.size()) q.pop(); cnt = sum = 0;
for(int i = 1; i <= n; ++i) {
q.push(m_p(a[i], 0));
if(q.top().first + b[i] + x <= 0) {
sum += q.top().first + b[i] + x;
if(!q.top().second) cnt++; q.pop();
q.push(m_p(-(b[i] + x), 1));
}
}
return cnt >= k;
}
signed main() {
freopen("world.in","r",stdin); freopen("world.out","w",stdout);
n = re(); k = re(); for_1(i, n) a[i] = re(); for_1(i, n) b[i] = re();
int l = -INF, r = INF;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) ans = mid, l = mid + 1;
else r = mid - 1;
}
check(ans); cout<<(sum - k * ans);
}

浙公网安备 33010602011771号