4.11省选模拟

1|04.11省选模拟

鸽子来更博客了。昨天太累的就咕了,什么时候有时间再补上吧。。

1|1T1 分头行动

我的 dp 状态有点奇怪,导致根本没有优化的空间,相当于是把自己送进了坟墓。。

首先一个很显然的性质,最后的序列一定是一个是前缀的最大值,另一个是不确定的。

那么我们由 f(i,j) 表示前 i 个物品,一个序列的最后一项是 j,另一个序列的最后一项是 s[i],即前缀最大值。

那么结合上面的性质,我们的转移可以分成下面几种。

  1. a[i]>s[i1] 那么肯定是放在另一个序列里面的,所以 f[i][j]+=a[i]

  2. a[i]s[i1] 又分为几种子情况

    1.a[i]j,那么其实相当于一个区间加等差数列 f[i][j]+=j

    2.a[i]jf[i][a[i]]=min(f[i1][j]+a[i]),其实相当于一个区间取反

    f[i][j]+=s[i1],这个相当于是选另外的那个序列

然后我们接着进行观察这个东西,发现其实就是要求你进行区间加等差数列,区间取 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 */

1|2T2 乱数假文

这个东西比较的神奇。好了下一题。

1|3T3 你的世界

是个题单里面的原题。但是我没做诶。

首先可以观察到的是这个每次配对的贡献如果看成一个函数的话,那么就是一个下凸的函数。因为一开始选择的点肯定是比较小的,后面的选择是越来越大的。因此这个函数具有凸性。

然后我们可以看到的是要求选 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); }

__EOF__

本文作者Zwaire
本文链接https://www.cnblogs.com/Zwaire/p/16132933.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Zwaire  阅读(29)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示