思路:我们可以这样构造,最大的那个肯定是作为以一个树根,所以我们只要找到一个序列a1 + a2 + a3 .... + ak 并且ak为
所有点中最大的那个,那么我们a1, a2, a3..., ak-1 作为单独的点,其他没有涉及到的点套在ak的里面。
现在问题变成了找出a1, a2, a3, a4, ... , ak。 可以用bitset优化普通dp,因为要找路径,空间开不下,所以需要分段。
#include<bits/stdc++.h> #define LL long long #define fi first #define se second #define mk make_pair #define PLL pair<LL, LL> #define PLI pair<LL, int> #define PII pair<int, int> #define SZ(x) ((int)x.size()) #define ull unsigned long long using namespace std; const int N = 70000 + 7; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; const int mod = 1e9 + 7; const double eps = 1e-8; int n, s; PII a[N]; bitset<70001> dp[N/10]; bitset<70001> dp2; bitset<70001> tmp; vector<int> edge[N]; vector<PII> dol; int c[N]; bool in[N]; bool ok(int x, int y) { if(y < 0) return false; dp2 = dp[x/10]; for(int i = x/10*10+1; i <= x; i++) dp2 |= (dp2 << a[i].fi); return dp2[y]; } int main() { scanf("%d%d", &n, &s); for(int i = 1; i <= n; i++) { scanf("%d", &a[i].fi); a[i].se = i; } sort(a+1, a+1+n); int tar = s - a[n].fi; tmp[0] = 1; dp[0] = tmp; for(int i = 1; i < n; i++) { tmp |= (tmp<<a[i].fi); if(i % 10 == 0) dp[i/10] = tmp; } if(ok(n-1, tar)) { for(int i = n-1, now = tar; i >= 1; i--) { if(now >= a[i].fi && ok(i-1, now-a[i].fi)) { now = now - a[i].fi; in[a[i].se] = true; } } for(int i = 1; i < n; i++) { if(in[a[i].se]) { c[a[i].se] = a[i].fi; } else { dol.push_back(a[i]); } } dol.push_back(a[n]); for(int i = 0; i < dol.size(); i++) { if(i) { c[dol[i].se] = dol[i].fi - dol[i-1].fi; edge[dol[i].se].push_back(dol[i-1].se); } else { c[dol[i].se] = dol[i].fi; } } for(int i = 1; i <= n; i++) { printf("%d %d ", c[i], edge[i].size()); for(int j : edge[i]) printf("%d ", j); puts(""); } } else { puts("-1"); } return 0; } /* */