/* 首先考虑点在直线的两边效果一样 于是转移到一边 之后发现当我们覆盖某些点时,有其他的一些点一定会被覆盖 我们找出所有必须覆盖的点 之后我们发现我们找到的这些点 将其按照x递增排序 那么y也是递增的 然后我们 可以得到转移方程了 令dp[i][j]表示前i个都覆盖到了,用了j个正方形的最小花费 然后我们考虑转移时的枚举dp[i][j] = min(dp[i][k] + (x[i] - y[k + 1] + 1) ^ 2; 这个是显然NKN的, 斜率优化后NK, wqs二分后Nlogm */ //#include "aliens.h" #include<cstring> #include<iostream> #include<algorithm> #include<cstdio> #include<vector> using namespace std; #define M 100010 #define ll long long #define inf 0x3f3f3f3f struct Note{ ll x, y; bool operator < (const Note &b) const{ return this->x == b.x ? this->y > b.y : this->x < b.x; } }note[M], p[M]; int tot,q[M],h,t, sm[M]; ll dp[M], ans; double X(int x) {return p[x + 1].y;} double Y(int x) {return dp[x] - 2 * p[x + 1].y + p[x + 1].y * p[x + 1].y;} double slope(int x, int y) {return (Y(x) - Y(y)) / (X(x) - X(y));} int solve(ll x) { h = 1, t = 1, q[1] = 0; for(int i = 1; i <= tot; i++) { while(h < t && 2.0 * p[i].x > slope(q[h], q[h + 1])) h++; dp[i] = dp[q[h]] + (p[i].x - p[q[h] + 1].y + 1) * (p[i].x - p[q[h] + 1].y + 1) + x; sm[i] = sm[q[h]] + 1; if(i == tot) break; if(p[i + 1].y <= p[i].x) dp[i] -= (p[i].x - p[i + 1].y + 1) * (p[i].x - p[i + 1].y + 1); while(h < t && slope(q[t], q[t - 1]) > slope(q[t], i)) t--; q[++t] = i; } return sm[tot]; } long long take_photos(int n, int m, int k, std::vector<int> r, std::vector<int> c) { for(int i = 0; i < n; i++) { note[i].y = r[i], note[i].x = c[i]; if(note[i].x < note[i].y) swap(note[i].x, note[i].y); } sort(note, note + n); for(int i = 1; i < n; i++) { while(h <= t && note[i].y <= note[q[t]].y ) t--; q[++t] = i; } for(int i = h; i <= t; i++){tot++;p[tot].x = note[q[i]].x, p[tot].y = note[q[i]].y;} if((ans = solve(0)) <= k) { return dp[tot]; } ll L = 0, R = 1ll * m * m + 10; while(L <= R) { ll mid = (L + R) >> 1; if(solve(mid) <= k) R = mid - 1, ans = dp[tot]; else L = mid + 1; } // if(ans - 1ll * R * k - k == 568367396612){solve(107455936237); return sm[tot];} return ans - 1ll * R * k - k; } /* 5 7 2 0 4 4 4 4 3 4 6 5 6 2 6 2 1 4 4 1 4 4 4 1 0 2 2 3 1 1 2 1 3 0 1 2 1 2 2 */