BZOJ 4349: 最小树形图

Description

\(n\)个节点,每个节点有一个攻击代价和需要攻击的次数。

有\(k\)个关系,攻击\(x\)后,\(y\)的攻击代价变成\(z\)。

Solution

朱刘算法。

这个好像就是求什么最小树形图的东东...

最小树形图跟最小生成树差不多,不过最小生成树是无向图,最小树形图是有向图,让一个节点和其他节点联通的最小代价。

这个建模也非常容易,只需要确定第一次的攻击即可,之后的一定都以最小代价攻击。

朱刘算法也很简单,步骤就是

找到每个节点的最小入边。

统计答案。

找环,缩环,把边权设成原边权-最小入边边权。

Code

/**************************************************************
    Problem: 2260
    User: BeiYu
    Language: C++
    Result: Accepted
    Time:8 ms
    Memory:1340 kb
****************************************************************/

#include <bits/stdc++.h>
using namespace std;

inline int in(int x=0,char ch=getchar()) { while(ch>‘9‘ || ch<‘0‘) ch=getchar();
    while(ch>=‘0‘ && ch<=‘9‘) x=x*10+ch-‘0‘,ch=getchar();return x; }

const int N = 55;
const int M = N*N+50;

struct Edge { int fr,to;double v; };
Edge edge[M];

int n,m,rt;
int b[N];
double mic[N];
double miv[N];
int pre[N],vis[N],id[N];

void AddEdge(int fr,int to,double v) { edge[++m]=(Edge){ fr,to,v }; }

double Solve(int n) {
    double res=0;
    for(int cnt,tmp;;) {
        for(int i=1;i<=n;i++) miv[i]=1e9,pre[i]=0;
        for(int i=1;i<=m;i++) {
            Edge &e=edge[i];
            if(e.v<miv[e.to]) miv[e.to]=e.v,pre[e.to]=e.fr;
        }
        for(int i=1;i<=n;i++) if(pre[i]) res+=miv[i];
        memset(vis,0,sizeof(vis));
        memset(id,0,sizeof(id));
        vis[0]=tmp=1,cnt=0;
        for(int i=1;i<=n;i++) if(!vis[i]) {
            ++tmp;int u=i;
            for(;!vis[u];u=pre[u]) vis[u]=tmp;
            if(vis[u]==tmp) {
                ++cnt;
                for(;!id[u];u=pre[u]) id[u]=cnt;
            }
        }
        if(!cnt) break;
        for(int i=1;i<=n;i++) if(!id[i]) id[i]=++cnt;
        int mm=m;m=0;
        for(int i=1;i<=mm;i++) {
            Edge &e=edge[i];
            if(id[e.fr]!=id[e.to]) AddEdge(id[e.fr],id[e.to],e.v-miv[e.to]);
        }
        n=cnt;
    }return res;
}

int main() {
    n=in(),rt=n+1;
    for(int i=1;i<=n;i++) {
        double x;scanf("%lf",&x);
        mic[i]=x;
        b[i]=in();
        if(b[i]) AddEdge(rt,i,x);
    }
    for(int k=in();k--;) {
        int x=in(),y=in();
        double z;scanf("%lf",&z);
        if(b[x] && b[y]) {
            AddEdge(x,y,z);
            mic[y]=min(mic[y],z);
        }
    }
    double ans=Solve(n+1);
//  cout<<ans<<endl;
//  for(int i=1;i<=n;i++) cout<<mic[i]<<" ";cout<<endl;
    for(int i=1;i<=n;i++) if(b[i]) ans+=(b[i]-1)*mic[i];
    printf("%.2lf\n",ans);
    return 0;
}

  

时间: 2025-01-20 07:22:12

BZOJ 4349: 最小树形图的相关文章

bzoj2260: 商店购物 &amp;&amp; 4349: 最小树形图

Description Grant是一个个体户老板,他经营的小店因为其丰富的优惠方案深受附近居民的青睐,生意红火.小店的优惠方案十分简单有趣.Grant规定:在一次消费过程中,如果您在本店购买了精制油的话,您购买香皂时就可以享受2.00元/块的优惠价:如果您在本店购买了香皂的话,您购买可乐时就可以享受1.50元/听的优惠价……诸如此类的优惠方案就是说:如果您在本店购买了商品A的话,您就可以以P元/件的优惠价格购买商品B(购买的数量不限).有趣的是,你需要购买同样一些商品,由于不同的购买顺序,Gr

bzoj2260: 商店购物&amp;&amp;4349: 最小树形图

最小树形图问题啊 最小树形图是撒哩,就是给你一个有向图,确定一个根,要你到达所有点,那棵最短路径树的总边权 做这个用的是朱(jv)刘(lao)算法. 首先假如有多个联通块就无解啦 对应每个点(除了根),找到一条连向它的最短的边,假如没有环,那这个就是答案嘛 否则就找环,然后缩点,对于一个环,假如要从它的一个成员节点x断开,那么答案是减去环上的边,然后加上连进来的边,那么我们就把所有连向x的边的权,减去环上这条边的权(感觉很像数据备份退流的思想) 不断重复直到没有环. #include<cstdi

【最小树形图(奇怪的kruskal)】【SCOI 2012】【bzoj 2753】滑雪与时间胶囊

2753: [SCOI2012]滑雪与时间胶囊 Time Limit: 50 Sec Memory Limit: 128 MB Submit: 1621 Solved: 570 Description a180285非常喜欢滑雪. 他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同一时候也是景点).并且每一个景点都有一编号i(1<=i<=N)和一高度Hi.a180285能从景点i 滑到景点j 当且仅当存在一条i 和j 之间的边,且i 的高度不小于j. 与其它滑雪爱好者不同,a1

bzoj4349: 最小树形图

最小树形图模板题…… 这种\(O(nm)\)的东西真的能考到么…… #include <bits/stdc++.h> #define N 60 #define INF 1000000000 using namespace std; int n, m, nn; double ai[N], an[N], ci[2][N][N], ans; int bc[N]; int ini[N], vis[N], inc[N], inl[N]; int dfn; int dfs(int t) { vis[t]

Directed_MST 最小树形图

List Directed_MST 最小树形图 List Knowledge 模板 Practice 参考资料 Knowledge 求一个图中遍历到每个点的方案中的最小边权和,显然n-1条边,即一颗树即可. 最小生成树?当然这里不是的,这里的最小树形图算法是针对有向图的. 最小树形图的第一个算法是1965年朱永津和刘振宏提出的复杂度为O(VE)的算法.简称朱刘算法. 1986年, Gabow, Galil, Spencer和Tarjan提出了一个复杂度更好的实现,其时间复杂度为O(E+VlogV

hdu2121+不定根最小树形图

算和定根最小树形图相同. 我们只需:设一个权值sumw=所有边之和+1,类似于网络流,向图中加入一个超级源点,把这个点作为虚根.虚根到其他所有点之间连上一条边,边权值为sumw. 求出的值减去sumw即为最小树形图的权值. 当然,返回-1则无解.此外,当求出的值>=2*sumw,也是无解的. 1 #include<cstdio> 2 #include<cstring> 3 using namespace std; 4 struct node 5 { 6 int u,v; 7

HDOJ 2121 Ice_cream’s world II 最小树形图无根树

朱刘算法 最小树形图无根树: 建立一个虚拟的根节点,向所有节点连边,权值为其他所有边的权值和+1 在求最小树形图的时候,记录和虚拟的根相连的是哪个节点 在这题中,边是从小往大加的所以直接记录的是相连的是第几号边.... Ice_cream's world II Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3442    Accept

最小树形图【模板】

基于贪心和缩点的思想. 假设根的顶点是V0. (1)除了根结点外,所有的点Vi,找到以Vi为终点的最短的边,加入集合中 (pre[v]存放的是终点v的起点,In[v]存放终点为v的最短的边) (2)检查集合中有没有有向环和收缩点.若没有有向环和收缩点,结束计算:若没有有向环.但含收缩边,则跳至步骤(4):若含有有向环,则跳至步骤(3).Ps:如果出现重边,将忽略权值较高的 (3)含有有向环,则收缩有向环(缩点),把有向环收缩为一个点,其有向环内的边被收缩掉,而环外的边被保 留,构建新图,重复步骤

HDU 2121 Ice_cream’s world II (不定根最小树形图)

题目地址:HDU 2121 这题没有给定根.最容易想到的当然是暴力,枚举所有的根,但是TLE是显然的..为了处理不定根的情况,可以虚拟一个根,然后用这个根去跟所有的点连边,权值为其他所有权值的和+1,目的是防止成为最小树形图的一条边.然后跑出最小树形图后,那么这个虚拟根肯定跟一个实际根相连,这时候根就找到了,然后再在最终的总花费中减去虚拟的那条边的权值就可以了. 代码如下: #include <iostream> #include <string.h> #include <m