AtCoderARC 098 F - Donation
Posted ivorysi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoderARC 098 F - Donation相关的知识,希望对你有一定的参考价值。
到AtCoder觉得整个人都需要交智商税了呢= =
题解
看完前面三道觉得很简单,代码也没写……看到最后的F题题解看了一上午……
就写一下F题吧
感觉这道题和之前见过的某道题有点一样,又不太一样……
智商--,看了好久题解才想明白一点。。。
就是……我们先想一下,我们如果按照每个点交钱的顺序写下一个序列
对于第k次选走的点,我们需要的是我们钱数(w),需要有(w >= sum_{i = 1}^{k}B_{id[i]} + max(A_{id[k]} - B_{id[k]},0))
最后我们需要的是序列最大值,我们要最大值最小
并且,要求这个序列付完钱的点,之后不会再经过了
我们对于每个点求一个(C[i])为(max(A[i] - B[i],0))
我们发现,如果一个在(C[i])最大值被选择之前,答案的下限在不断提高
那么我们可能会考虑到,我们要拿走u点之后,我们的图可能会裂成很多子图,我们需要进入其中一个子图,然后将剩下子图的B连带u点累加起来加上(C[u])来累加答案
那么,在最大值拿走之前,假设最大值拿走之后我们进入的子图是(G_{i}),那么,(G_{i})里的任何一点(w)为什么不能先于u之前拿走呢?
这是我很难想明白的一件事,我现在也许能谈一下理解
当(w)点在(u)之前时,(B[w])必定会被统计到答案的一个下限中,如果(w)在(u)点之后,(B[w])可能不会累加进最后的答案用来更新,也可能会,而我们希望的是答案更优秀,(w)在(u)之前答案不可能变小,而(w)在(u)之后,答案可能会变小但是绝对不会变大,所以我们把其他子图访问完之后选择访问这个子图
我们发现,这刚刚好是一个子问题,我们对这个子图取出最大值作为根,然后重新求一下这个子图的答案就可以,我们可以用dp解决这个问题,对于每个子图都进入一遍求值
然而,好像又有了一个新的问题,我们怎么求每次去掉最大值后,每个子图里的最大值,暴力求显然是(n^2)的
我们发现,全局最大值和每个子图里的最大值连边,每个子图里的最大值和去掉最大值后新裂成的子图最大值连边,刚好可以在图中构建出一棵树
而C值最小的点,必然是叶子
我们按C从小到大遍历每个点,访问周边的所有点,如果周边的点有C值小于它的,就让周边的点所在的子图的最大值(用并查集维护)作为它的儿子就好了
写出来的代码非常清晰,又简单好写,思维含量又高,可以说是虐我这种蒟蒻的好题了。
代码
#include <bits/stdc++.h>
//#define ivorysi
#define enter putchar(‘n‘)
#define space putchar(‘ ‘)
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define eps 1e-8
#define mo 974711
#define MAXN 200005
#define pii pair<int,int>
using namespace std;
typedef long long int64;
typedef double db;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < ‘0‘ || c > ‘9‘) {
if(c == ‘-‘) f = -1;
c = getchar();
}
while(c >= ‘0‘ && c <= ‘9‘) {
res = res * 10 + c - ‘0‘;
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {putchar(‘-‘);x = -x;}
if(x >= 10) {
out(x / 10);
}
putchar(‘0‘ + x % 10);
}
int N,M;
struct node {
int to,next;
}E[MAXN * 2];
int head[MAXN],sumE,id[MAXN],ra[MAXN],f[MAXN];
int64 A[MAXN],B[MAXN],C[MAXN],dp[MAXN],siz[MAXN];
vector<int> son[MAXN];
int getfa(int x) {
return f[x] == x ? x : f[x] = getfa(f[x]);
}
bool cmp(int s,int t) {
return C[s] < C[t];
}
void add(int u,int v) {
E[++sumE].to = v;
E[sumE].next = head[u];
head[u] = sumE;
}
void Init() {
read(N);read(M);
for(int i = 1 ; i <= N ; ++i) {
read(A[i]);read(B[i]);C[i] = max(A[i] - B[i],0LL);
id[i] = i;
}
sort(id + 1,id + N + 1,cmp);
for(int i = 1 ; i <= N ; ++i) ra[id[i]] = i;
int u,v;
for(int i = 1 ; i <= M ; ++i) {
read(u);read(v);
add(u,v);add(v,u);
}
}
void dfs1(int u) {
siz[u] += B[u];
for(auto v : son[u]) {
dfs1(v);
siz[u] += siz[v];
}
}
void dfs2(int u) {
if(son[u].size() == 0) {
dp[u] = B[u] + C[u];
return;
}
dp[u] = 1e18;
for(auto v : son[u]) {
dfs2(v);
dp[u] = min(siz[u] - siz[v] + max(C[u],dp[v]),dp[u]);
}
}
void Solve() {
for(int i = 1 ; i <= N ; ++i) f[i] = i;
for(int i = 1 ; i <= N ; ++i) {
int u = id[i];
for(int j = head[u] ; j ; j = E[j].next) {
int v = E[j].to;
if(ra[getfa(v)] < ra[u] && getfa(v) != u) {
son[u].pb(getfa(v));
f[getfa(v)] = u;
}
}
}
dfs1(id[N]);
dfs2(id[N]);
out(dp[id[N]]);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}
以上是关于AtCoderARC 098 F - Donation的主要内容,如果未能解决你的问题,请参考以下文章
AtCoderARC067 F - Yakiniku Restaurants 单调栈+矩阵差分