@AkamemakA
2022-09-04T01:17:53.000000Z
字数 1854
阅读 140
Atcoder 题解
题目链接:点我
给定一张点边的简单无向图,每个点有一个点权。进行次操作,每次操作会删除一个点,删除一个点的代价是:这个点的所邻点的点权和。然后对于后续操作,这个点与连接到这个点的所有边都不复存在。问次操作中,单次代价的最大值最小可以是多少?
4 33 1 4 21 21 34 1
3
通过如下次操作,我们可以得到最小的最大值:
可以证明,没有一个方案得到的最大值比更小。
7 13464 661 847 514 74 200 1885 17 15 74 14 52 45 21 31 63 51 24 62 7
1199
求最大值最小,二分??
其实,这道题是一道贪心。我们仔细想想,我们要找到最小的最大值,那么我们在每次删点操作时,肯定是更贪心的选择所有未删的点中花费代价最少的点。
所以,每次操作只要找到所有点中花费代价最少的点即可。
但是怎么找呢?双层枚举, ?肯定会炸。因此,我们必须来找一些优化的技巧。
对于快速找到最小值,且可以插入删除指定元素的容器,你们想到了什么?没错,那就是。
在读入时,我们就可以处理出删除一个点所需要花费的代价。
我们把所有未删的点都插入容器中,那么每次删点操作直接取第一个元素即可。因为内部已经按照从小到大排好了序。
那么删点之后如何更新呢?
我们可以枚举这个点的所有邻点,然后这些邻点的原有代价减去这个点的点权即可。因为这个点被删除,那么它的邻点被删除时,所需要计算的点会少了当前这一个点。(如果不好理解,可以看代码并画图模拟一下。)
总时间复杂度。
#include<iostream>#include<algorithm>#include<set>using namespace std;#define int long longconst int N=4e5+5;typedef pair<int,int> pii;int n,m,val[N],ans;int he[N],ne[N],go[N],tot;set<pii> s;//存储未删除的点。因为我们需要知道当前删除的点的编号,因此采用pair将代价与编号捆绑起来。int deg[N]; //deg[i]表示删除i号点所需要的代价。bool vis[N]; //这里有一个小细节:我们在删一个点,枚举其邻点时,必须要判断这个点是否已经被删除。否则会WA得不知所措(悲inline void add(int a,int b){ne[++tot]=he[a];he[a]=tot;go[tot]=b;}//前向星建图signed main(){cin>>n>>m;for(int i=1;i<=n;i++)cin>>val[i];for(int i=1;i<=m;i++){int u,v;cin>>u>>v;add(u,v);add(v,u);deg[u]+=val[v];deg[v]+=val[u]; //处理deg数组}for(int i=1;i<=n;i++) s.insert({deg[i],i}); //开始所有的点都未被删除for(int i=1;i<=n;i++){auto t=s.begin();s.erase(t); //取set首元素并删除pii res=*t;//迭代器转piivis[res.second]=1;//标记该点已被删除ans=max(ans,deg[res.second]);//用删去这个点所需要的代价更新答案for(int j=he[res.second];j;j=ne[j]){int v=go[j]; //枚举邻点if(vis[v]) continue; //如果这个点已经被删除,则跳过s.erase({deg[v],v}); //取出编号为v的这个点deg[v]-=val[res.second]; //更新删去这个点所需要的的代价s.insert({deg[v],v}); //放回这个点}//set会自动排序,以便下一次删点。}cout<<ans;}
一定要判断是否被删去!!!血的教训
完结撒花~~~