[每日随题5] 贪心 - 树型DP - 邓老师调整法
整体概述
- 难度:900 -> 2000 -> 3000
1468N.Waste Sorting
-
标签:贪心
-
前置知识:无
-
难度:ICPC.N 900
题目描述:

输入格式:

输出格式:

样例输入:
7
1 2 3
1 2 3 0 0
2 2 3
1 2 3 1 0
2 2 3
1 2 3 0 1
1 2 5
1 2 3 1 1
0 0 0
0 0 0 0 0
0 0 4
1 0 0 0 0
13 37 42
0 0 0 40 47
样例输出:
YES
YES
NO
YES
YES
NO
YES
解题思路:
-
对于前三类物品,直接丢入对应的垃圾桶内。
-
对于第四、五两类物品,优先丢到前两种垃圾桶内,前两种不够丢的时候丢到第三个垃圾桶中。
-
当某个垃圾桶不够丢时,则输出
NO,否则输出YES。
完整代码
#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 5e5+5;
inline string solve(){
int c[4],a[6];
for(int i=1;i<=3;i++) cin >> c[i];
for(int i=1;i<=5;i++) cin >> a[i];
for(int i=1;i<=3;i++) c[i] -= a[i];
if(c[1]>0){
int x = min(c[1],a[4]);
c[1] -= x, a[4] -= x;
}
if(c[2]>0){
int x = min(c[2],a[5]);
c[2] -= x, a[5] -= x;
}
c[3] -= a[4] + a[5];
for(int i=1;i<=3;i++) if(c[i]<0) return "NO";
return "YES";
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; cin >> T;
while(T--) cout << solve() << '\n';
return 0;
}
1363E.Tree Shuffling
-
标签:树型DP
-
前置知识:链式前向星
-
难度:Div.2.E 2000
题目描述:

输入格式:

输出格式:

样例输入:
5
1 0 1
20 1 0
300 0 1
4000 0 0
50000 1 0
1 2
2 3
2 4
1 5
5
10000 0 1
2000 1 0
300 0 1
40 0 0
1 1 0
1 2
2 3
2 4
1 5
2
109 0 1
205 0 1
1 2
样例输出:
4
24000
-1
解题思路:
-
由于操作可以选择任意一个节点 \(u\) 将其所有子节点进行操作,显然如果父节点的价格比子节点更低,直接在父节点上进行所有该子节点的操作是等价的。
那么我们进行一遍 \(DFS\) 直接将每个节点的价格重置为其所有父节点中最便宜的价格,所有在该子节点上的操作直接使用该子节点重置后的价格即可。
-
随后考虑如何计算匹配所有节点的最小价格。我们节点需要从 \(0->1\) 为状态 \(1\),需要从 \(1->0\) 为状态 \(2\),则我们只需要将所有状态 \(1\) 和状态 \(2\) 的节点一一其数值即可。
-
那么我们从叶节点往上,每个节点记录自己的子树上有多少个状态 \(1\) 和状态 \(2\) 的节点,处理完后返回所用的价格,两种状态的节点的剩余数量给自己的父节点,让其处理剩余的部分。最后如果根节点处的价格即处理完所有节点的总价格,
-
如果根节点仍然存在状态 \(1\) 或者状态 \(2\) 的节点,则输出 \(-1\)。否则直接输出根节点的总价即可。
-
总复杂度 \(O(n)\)。
完整代码
#include<bits/stdc++.h>
#define Size(x) ((int)(x).size())
#define int long long
using namespace std;
const int N = 2e5+5;
int n,val[N],ha[N],idx;
bool b[N],c[N];
struct Edge{int to,ne;}edge[N<<1];
inline void ins(int u,int v){
edge[++idx] = {v,ha[u]}, ha[u] = idx;
}
struct Info{
int cost,cnt1,cnt2; // 1:1->0 2:0->1
Info(int cost=0,int cnt1=0,int cnt2=0):cost(cost),cnt1(cnt1),cnt2(cnt2){}
Info operator + (const Info &x){
return Info(cost+x.cost,cnt1+x.cnt1,cnt2+x.cnt2);
}
};
inline void dfs1(int u,int par){
if(u != 1) val[u] = min(val[u],val[par]);
for(int i=ha[u];i;i=edge[i].ne){
int v = edge[i].to;
if(v != par) dfs1(v,u);
}
}
inline Info dfs2(int u,int par){
Info res;
if(b[u] && !c[u]) res.cnt1 += 1;
if(!b[u] && c[u]) res.cnt2 += 1;
for(int i=ha[u];i;i=edge[i].ne){
int v = edge[i].to;
if(v == par) continue;
res = res + dfs2(v,u);
}
int cnt = min(res.cnt1,res.cnt2);
res.cost += 2*cnt*val[u];
res.cnt1 -= cnt, res.cnt2 -= cnt;
return res;
}
inline void solve(){
cin >> n;
for(int i=1;i<=n;i++) cin >> val[i] >> b[i] >> c[i];
for(int i=1,u,v;i<n;i++){
cin >> u >> v;
ins(u,v),ins(v,u);
}
dfs1(1,0);
Info res = dfs2(1,0);
if(res.cnt1 || res.cnt2) cout << -1;
else cout << res.cost;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T; T = 1;
while(T--) solve();
return 0;
}
280E.Sequence Transformation
-
标签:邓老师调整法
-
前置知识:差分数组
-
难度:Div.1.E 3000
题目描述:

输入格式:

输出格式:

样例输入:
3 6 2 2
1 4 6
10 100000 8714 9344
3378 14705 17588 22672 32405 34309 37446 51327 81228 94982
样例输出:
1.666667 3.666667 5.666667
0.666667
1.000000 8715.000000 17429.000000 26143.000000 34857.000000 43571.000000 52285.000000 61629.000000 70973.000000 80317.000000
797708674.000000
解题思路:
-
我们的目标是构造出一个满足条件的序列,使得代价尽可能小。可以尝试先构造出一个满足条件的解,然后将其慢慢调整至最优解。
-
我们设数组 \(y\) 的差分为数组 \(d\),那么我们可以构造出 \(y_1 = 1\),\(d_i = a\) 的一个合法序列,然后慢慢尝试调大每个 \(d_i\),达到最小的答案。
-
那么我们需要考虑,将某个位置的 \(d_k\) 增大 \(v\) 会带来的影响。原先每个位置 \(i\) 的代价为 \((y_i-x_i)^2\),则使一个位置 \(d_k\) 增大 \(v\) 带来的影响为 \(\sum_{i=k}^{n}(y_i-x_i+v)^2-(y_i-x_i)^2\) = \((n-k+1)*v^2+2*v*\sum_{i=k}^{n}(y_i-x_i)\)。
-
对于相同的增加量 \(v\),想要使答案减小的最多,那么需要 \((n-k+1)\) 尽可能小,\(\sum_{i=k}^{n}(y_i-x_i)\) 为负数且尽可能小。所以我们每次找到 \(\sum_{i=k}^{n}(y_i-x_i)\) 最小且为负数的位置 \(k\),将其增加到不是最小值或者到达限制。
-
最后统计出所有的 \(y_i\),再统计出所有的代价即可。
完整代码
#include <bits/stdc++.h>
#pragma optimize(3)
#define lb long double
using namespace std;
const int N = 2e5+5; const lb eps = 1e-5;
int n,q,a,b,x[N];
lb d[N],y[N],s[N];
signed main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cout << fixed << setprecision(6);
cin >> n >> q >> a >> b;
for(int i=1;i<=n;i++) cin >> x[i];
d[1] = 1;
for(int i=2;i<=n;i++) d[i] = a;
lb rest = q-1 - a*(n-1); // 最大 q 的限制
while(rest){
y[1] = d[1];
for(int i=2;i<=n;i++) y[i] = y[i-1] + d[i];
for(int i=n;i>=1;--i) s[i] = s[i+1] + y[i]-x[i];
int p = 1;
for(int i=2;i<=n;i++) if(s[i]<=s[p]+eps && d[i]+eps<=b) p = i;
if(s[p] >= 0) break;
lb v = min(-s[p]/(n-p+1),rest); // s[p]<0的限制 与 rest的限制
if(p != 1) v = min(v,b-d[p]); // 上界b的限制
for(int i=p+1;i<=n;i++) if(d[i]+eps<=b) v = min((s[i]-s[p])/(i-p),v); // 最多增加到非最小 s[p]
d[p] += v, rest -= v;
}
y[1] = d[1];
for(int i=2;i<=n;i++) y[i] = y[i-1]+d[i];
lb ans = 0;
for(int i=1;i<=n;i++){
cout << y[i] << ' ';
ans += (y[i]-x[i])*(y[i]-x[i]);
}
cout << '\n' << ans <<'\n';
return 0;
}

每日练题中,挑战挑战呀~~
浙公网安备 33010602011771号