[噼昂!]fake(Pending)
阿新 • • 發佈:2021-10-08
目錄
,即第二維按照 \(\bmod 12\) 分組,每組都是凸的。
,並且一定可以構造出一種劃分使得 \(f(i, j + 12) \ge \frac{f(i, j) + f(i, j + 24)}{2}\),至於構造可以自己想一想,這裡不再贅述。於是我們證明了它的凸性。
壹、關於題目 ¶
懶得。
貳、關於題解 ¶
這種奇奇怪怪的凸性是真的想不到。
我們不妨先將下標從 \(0\) 開始編,於是每件物品的重量都在 \([0,4]\) 之間。然後開始樸素 \(\rm DP\):定義 \(f(i,j)\) 表示前 \(i\) 組物品,共選擇重量為 \(j\) 時的最大價值,轉移就不用說了。
顯然樸素的轉移會讓複雜度達到 \(\mathcal O(n^2)\),這是不可接受的。不過這個 \(\rm DP\) 有奇奇怪怪的凸性:\(f(i, j + 24) - f(i, j + 12) \ge f(i, j + 12) - f(i, j)\)
至於證明,先有引理:
對於任一整數可重集 \(S=\{s_i\}\),若 \(\sum s_i=24\) 且 \(\forall i,s_i\in [0,4]\),則一定存在一種劃分,使得劃分之後的兩個集合 \(S_1,S_2\) 滿足:
- \(S_1\cup S_2=S\land S_1\cap S_2=\emptyset\);
- \(\sum_{x\in S_1}x=12,\sum_{y\in S_2}y=12\);
那麼,對於任意由 \(f(i,j)\) 到 \(f(i, j + 24)\) 的情形,我們一定可以拼出一種 \(f(i, j + 12)\)
然後就很好做了,我們對於第一維分治,將兩個區間的凸包集合合併,由於合併的都是凸包,於是可以使用閔科夫斯基和的方法。
總複雜度 \(\mathcal O(k^2\times \frac{n}{k}\log n)=\mathcal O(kn\log n)\).
叄、參考程式碼 ¶
#pragma GCC optimize("Ofast") #include <bits/stdc++.h> using namespace std; // # define USING_STDIN // # define NDEBUG // # define NCHECK #include <cassert> namespace Elaina { #define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i) #define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i) #define fi first #define se second #define mp(a, b) make_pair(a, b) #define Endl putchar('\n') #define whole(v) ((v).begin()), ((v).end()) #define bitcnt(s) (__builtin_popcount(s)) #ifdef NCHECK # define iputs(Content) ((void)0) # define iprintf(Content, argvs...) ((void)0) #else # define iputs(Content) fprintf(stderr, Content) # define iprintf(Content, argvs...) fprintf(stderr, Content, argvs) #endif typedef unsigned int uint; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> pii; typedef pair<ll, ll> pll; template<class T> inline T fab(T x) { return x < 0 ? -x : x; } template<class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); } template<class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); } #ifndef USING_STDIN inline char freaGET() { # define BUFFERSIZE 1 << 18 static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF; return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++; # undef BUFFERSIZE } # define CHARGET freaGET() #else # define CHARGET getchar() #endif template<class T> inline T readret(T x) { x=0; int f = 0; char c; while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1; for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48)); return f ? -x : x; } template<class T> inline void readin(T& x) { x = readret(T(1)); } template<class T, class... Args> inline void readin(T& x, Args&... args) { readin(x), readin(args...); } template<class T> inline void writc(T x, char s = '\n') { static int fwri_sta[55], fwri_ed = 0; if(x < 0) putchar('-'), x = -x; do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x); while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed); putchar(s); } } using namespace Elaina; const int maxn = 1e5; vector<ll> a[maxn + 5]; int pre[maxn + 5], n; inline void input() { readin(n); int k; rep(i, 1, n) { readin(k), pre[i] = pre[i - 1] + k - 1; a[i].resize(k); rep(j, 0, k - 1) readin(a[i][j]); } } struct Hull_Set { vector<ll> v[12]; inline vector<ll>& operator [](const int id) { return v[id]; } }; // Minkowski inline void Merge(Hull_Set a, Hull_Set b, Hull_Set& res) { for(int i = 0; i < 12; ++i) if(!a[i].empty()) for(int j = 0; j < 12; ++j) if(!b[j].empty()) { int delta = (i + j >= 12), nxt = (i + j) % 12; int x = 0, y = 0; while(1) { assert(x + y + delta < res[nxt].size()); res[nxt][x + y + delta] = max(res[nxt][x + y + delta], a[i][x] + b[j][y]); if(x + 1 == a[i].size() && y + 1 == b[j].size()) break; if(x + 1 == a[i].size()) ++y; else if(y + 1 == b[j].size()) ++x; else { if(a[i][x + 1] - a[i][x] > b[j][y + 1] - b[j][y]) ++x; else ++y; } } } } Hull_Set solve(int l, int r) { if(l == r) { Hull_Set ret; for(int i = 0; i < (int)a[l].size(); ++i) ret[i].push_back(a[l][i]); return ret; } int mid = l + r >> 1; auto _lhs = solve(l, mid), _rhs = solve(mid + 1, r); Hull_Set ret; for(int i = 0; i < 12; ++i) { for(int j = i; j <= pre[r] - pre[l - 1]; j += 12) ret[i].push_back(-1); } Merge(_lhs, _rhs, ret); return ret; } signed main() { // freopen("fake.in", "r", stdin); // freopen("fake.out", "w", stdout); input(); auto res = solve(1, n); rep(i, 0, pre[n]) writc(res.v[i % 12][i / 12], ' '); return 0; }
肆、關鍵 の 地方 ¶
證明凸性的另一種方法 —— 中間的點比左右倆點的中值都 大/小,則一定是有凸性的。