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); }