这个战斗的情况非常的不正常,如果怪物不能破防还会给你加血。
于是我们可以和怪物战斗 \(\lceil\frac{h_i}{X-d_i}\rceil\) 回合,假设现在的防御力为 \(Y\),那么收益就是:
对于每一个怪物,我们即 \(c_i=\lceil\frac{h_i}{X-d_i}\rceil\),那么显然 \(-c_i\times a_i\) 是肯定会造成的,我们只需要最大化 \(Y\times c_i\) 就行了。
这个问题可以加强成为下面的问题,最大化下式:
其中 \((a_i,b_i)\) 表示第 \(i\) 个点可以增加多少防御力,\(b_i\) 表示这个节点的回合数。
考虑如果 \(a_i\times b_j>a_j\times b_i\),那么需要满足 \(\frac{a_i}{b_i}>\frac{a_j}{b_j}\),于是我们把这个东西作为关键在,排序之后计算肯定是最优的。
显然这是一个全序问题,考虑在放到树上之后怎么做。
其实是经典贪心,类似于 https://vjudge.net/problem/HDU-6326。
感性理解,如果某个父亲如果没有其儿子优,那么我们取选择父亲肯定是为了选择儿子,于是我们直接把父亲和儿子绑定到一起,让父亲选完放上就拿儿子。
考虑通过调整法证明其正确性,我们假设存在一个父亲 \(fa\) 在没有儿子 \(x\) 优的情况下,没有马上选择 \(x\) 而是又去选择了另外一个节点 \(p\)。
如果 \(p\) 比 \(x\) 优,那么我们先选择 \(p\) 再选择 \(fa\) 一定更优。
如果 \(p\) 比 \(fa\) 裂,那么最后选择 \(p\) 是更优的。
如果 \(p\) 在 \(x\) 和 \(fa\) 之间,那么其可能先于 \(fa\) 选择或者后于 \(p\),因为这至少可以造成 \(1\) 的贡献,而在中间选择什么都得不到。
我们这样操作是不会影响其他元素的,我们这些操作只会涉及 \(fa,p\) 的这个区间的选择进行交换,并不会把其他的元素交换进来。
于是就赢了,时间复杂度为 \(O(n\log n)\)。
#include<iostream>
#include<queue>
#include<vector>
#include<bitset>
#define int long long
using namespace std;
const int N=1e5+5,inf=1e18;
int n,A,F[N],a[N],b[N],ans,p;
vector<int> v[N];
bitset<N> vis;
struct node{int a,b,id;friend bool operator <(const node a,const node &b){if (a.a*b.b!=a.b*b.a) return a.a*b.b<a.b*b.a;return a.a+inf*(!a.a&&!a.b)<b.b+inf*(!b.a&&!b.b);}
};
struct DSU{int fa[N];DSU(){for(int i=1;i<N;i++)fa[i]=i;}int f(int x){return fa[x]=(fa[x]==x?x:f(fa[x]));}void merge(int x,int y){fa[f(x)]=f(y);}
}dsu;
void dfs(int x,int f){F[x]=f;for(int i:v[x]) if(i!=f) dfs(i,x);
}
priority_queue<node>q;
signed main(){ios::sync_with_stdio(false);cin.tie(nullptr);cin>>n>>A;for(int i=1,x,y;i<n;i++){cin>>x>>y;v[x].push_back(y);v[y].push_back(x);}dfs(1,0);for(int i=2,op,x,d,h;i<=n;i++){cin>>op;if(op==1){a[i]=1;}else{cin>>x>>d>>h,d=A-d;int t=(h+d-1)/d-1;ans-=t*x,b[i]=t;}}for(int i=1;i<=n;i++){q.push({a[i],b[i],i});}while(!q.empty()){auto [x,y,o]=q.top();q.pop();if(!vis[o]&&x==a[o]&&y==b[o]){p=dsu.f(F[o]),vis[o]=1,dsu.merge(o,F[o]);ans+=a[p]*y,a[p]+=x,b[p]+=y;if(p>1) q.push({a[p],b[p],p});}}cout<<ans<<'\n';return 0;
}