習題:Alternating Tree(樹DP)
A
首先前三個符合題意的數字是 \(6\) \(10\) \(14\)\((2*3 ,2*5,2*7)\)
那麼最小的數字一定需要大於30,因為n必須由正整陣列成。
所以我們可以一開始放置\(6\) \(10\) \(14\)三個數字,然後再使用n-30即可。
注意因為數字不能相同,所以需要判斷一下,有相同的數字的話將14換成15。因為\(10-6=4,14-10=4\)故某一個數字增大了1,那麼不可能再有相等的數字存在。
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<climits> #include<stack> #include<vector> #include<queue> #include<set> #include<bitset> #include<map> #include<regex> #include<cstdio> #include <iomanip> #pragma GCC optimize(2) #define up(i,a,b) for(int i=a;i<b;i++) #define dw(i,a,b) for(int i=a;i>b;i--) #define upd(i,a,b) for(int i=a;i<=b;i++) #define dwd(i,a,b) for(int i=a;i>=b;i--) #define local typedef long long ll; typedef unsigned long long ull; const double esp = 1e-6; const double pi = acos(-1.0); const int INF = 0x3f3f3f3f; const int inf = 1e9; using namespace std; ll read() { char ch = getchar(); ll x = 0, f = 1; while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } typedef pair<int, int> pir; #define lson l,mid,root<<1 #define rson mid+1,r,root<<1|1 #define lrt root<<1 #define rrt root<<1|1 int t, n; int main() { t = read(); while (t--) { n = read(); if (n <= 30) { printf("NO\n"); } else { int a = 6, b = 10, c = 14, d = n - 30; if (d == a || d == b || d == c) { if (n <= 31) { printf("NO\n"); continue; } else { c = 15; d = n - 31; } } printf("YES\n%d %d %d %d\n",a,b,c,d); } } return 0; }
B
首先每一個位置不是9就是8,因為這樣二進位制位數才是4的倍數,同時保證了總的二進位制個數最多。然後是何時放置8的問題。因為我們需要最小的數字,所以當\(n\mod4!=0\)會破壞掉某一個9,或者8的四位二進位制數字的某幾位。這個時候我們把9換成8即可。所以先假定全部都是9,然後求n會破壞掉幾塊,再把那幾塊對應的位置全部置換成8。
#include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<cstdlib> #include<climits> #include<stack> #include<vector> #include<queue> #include<set> #include<bitset> #include<map> #include<regex> #include<cstdio> #include <iomanip> #pragma GCC optimize(2) #define up(i,a,b) for(int i=a;i<b;i++) #define dw(i,a,b) for(int i=a;i>b;i--) #define upd(i,a,b) for(int i=a;i<=b;i++) #define dwd(i,a,b) for(int i=a;i>=b;i--) #define local typedef long long ll; typedef unsigned long long ull; const double esp = 1e-6; const double pi = acos(-1.0); const int INF = 0x3f3f3f3f; const int inf = 1e9; using namespace std; ll read() { char ch = getchar(); ll x = 0, f = 1; while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x * f; } typedef pair<int, int> pir; #define lson l,mid,root<<1 #define rson mid+1,r,root<<1|1 #define lrt root<<1 #define rrt root<<1|1 int t, n; int main() { t = read(); while (t--) { n = read(); { { int pos = n / 4; int mod = n % 4; if (mod)pos++; while (n > pos) { printf("9"); n--; } while (pos--)printf("8"); puts(""); } } } return 0; }
C
首先進行子樹大小的計算,這裡的子樹大小規定為當前節點下面,一共有多少人,即每個點的流量。
我們知道了每一個點的流量,假設是\(tot\)。那麼就可以寫出方程式
\(happy-bad=h_i\) \(happy+bad=tot\),解出\(happy\)和\(bad\)首先判斷他們的正確性,不是整數,小於零,都不行。然後進行樹上貪心。可以看出,當從一個節點向下走到子節點的時候,如果全部都是\(happy\)那麼一定可行,因為我們可以變化任意數量的人到\(badmoon\)。然後是如果流量裡面帶了\(bad\),那麼這個\(bad\)值一定不能大於子節點的\(bad\)值,這個時候我們只需要變化出流量-\(bad\)
假定我們執行到\(u\)節點。設\(u\)的子節點是\(v'_{i}\),當前節點有一個\(bad\)值
1.當\(u\)節點能夠容納的人數>=\(bad\)的時候,我們將\(bad\)全部放在u節點。
2.噹噹\(u\)節點能夠容納的人數<\(bad\)的時候,我們判斷\(\sum{v'_i[bad]}\)是否能夠滿足\(bad_u-people[u]\),如果能夠,那麼我們就將\(bad_u-people[u]\)這麼多人數,在從\(u\)走到\(v\)變成\(bad\),因為已經計算\(\sum v'_i[bad]\)能夠容納,那麼肯定符合題意,否則不行。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
char ch = getchar(); ll x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 1e5 + 10;
int T, n, m;
vector<int>vec[N];
ll p[N], h[N];
ll hm[N], bm[N];
void addedge(int u, int v)
{
vec[u].push_back(v);
vec[v].push_back(u);
}
ll sz[N];
void dfs1(int u, int fa) {
sz[u] = p[u];
for (auto k : vec[u])
{
if (k == fa)continue;
dfs1(k, u);
sz[u] += sz[k];
}
}
bool calc() {
upd(i, 1, n)
{
if ((h[i] + sz[i]) % 2ll) { return 0; }
hm[i] = (h[i] + sz[i]) / 2ll;
bm[i] = sz[i] - hm[i];
if (hm[i] < 0 || bm[i] < 0)return 0;
}
return 1;
}
bool flag = 0;
void dfs2(int u, int fa)
{
if (flag)return;
if (p[u] < bm[u]) {
ll tot = 0;
bm[u] -= p[u];
vector<int>tp;
for (auto k : vec[u])
{
if (k == fa)continue;
tot += bm[k];
tp.push_back(k);
}
if (tot < bm[u]) {
flag = 1; return;
}
/*sort(tp.begin(), tp.end(), [](int a, int b) {return bm[a] < bm[b]; });
dwd(i, tp.size() - 1, 0)
{
if (bm[tp[i]] >= bm[u])
{
bm[u] = 0; bm[tp[i]] -= bm[u]; break;
}
else {
bm[tp[i]] = 0; bm[u] -= bm[tp[i]];
}
}*/
}
for (auto k : vec[u])
{
if (k == fa)continue;
dfs2(k, u);
}
}
int main()
{
T = read();
while(T--)
{
n = read(), m = read();
flag = 0;
upd(i, 0, n)vec[i].clear();
upd(i, 1, n)p[i] = read();
upd(i, 1, n)h[i] = read();
int u, v;
upd(i, 1, n - 1) {
u = read(), v = read();
addedge(u, v);
}
dfs1(1, 0);
if (calc()) {
dfs2(1, 0);
if (flag)printf("NO\n");
else printf("YES\n");
}
else {
printf("NO\n");
}
}
return 0;
}
D
由題意,\(b[n]\)陣列實際上我們通過觀察可以發現,我們把\(-1\)也抽象成一個節點,就有\(n+1\)個點和\(n\)條邊。又因為題意保證了沒有環,且總能夠走到\(-1\),所以是一棵樹。所以將該陣列抽象成一棵樹,進行樹上dp。
令\(dp[u]\)表示u節點所能夠達到的最大值。又因為\(u\)節點的值,當且僅能被所有的鄰接子節點更新,假設是\(v'_i\),就有:
\(dp[u]+=dp[v'_i]>=0?dp[v'_i]:0\)。
又因為答案需要輸出方案,所以我們開兩個指標,一個從前到後\(pre\),一個從後往前\(suf\)。當\(dp[u]被v'_i\)更新的時候,更新到\(pre\)指標,否則更新到\(suf\)指標。
合理性顯然,因為我們自底而上,每次更新當前節點的時候,時間一定會在子節點的後面或者之前。將合併的資訊放前面,不合並的點放後面,就不會有任何的影響。
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b) for(int i=a;i<b;i++)
#define dw(i,a,b) for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
char ch = getchar(); ll x = 0, f = 1;
while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 2e5 + 10;
vector<int>vec[N];
void addedge(int u, int v)
{
vec[u].push_back(v);
vec[v].push_back(u);
}
int n;
ll a[N];
ll b[N];
ll dp[N];
int ans[N]; int cnt = 0; int last = 0;
ll sum = 0;
int sz[N];
void dfs2(int u, int fa)
{
sz[u] = 1;
for (auto k : vec[u])
{
if (k == fa)continue;
dfs2(k, u);
sz[u] += sz[k];
}
}
void dfs(int u, int fa)
{
dp[u] = a[u];
for (auto k : vec[u])
{
if (k == fa)continue;
dfs(k, u);
if (dp[k]>=0) {
dp[u] += dp[k];
ans[++cnt] = k;
}
else {
ans[last--] = k;
}
}
if(u!=0)
sum += dp[u];
}
int main()
{
n = read();
upd(i, 1, n)a[i] = read();
last = n;
upd(i, 1, n)
{
b[i] = read();
addedge(i, b[i] == -1 ? 0 : b[i]);
}
dfs2(0, 0);
dfs(0, 0);
cout << sum << endl;
upd(i, 1, n)
{
printf("%d ", ans[i]);
}
return 0;
}