• 「国庆训练」Bomb(HDU-5934)


    题意

    给定(n)个炸弹,每个炸弹的坐标与代价与影响范围给定,炸弹会引爆影响范围内其他所有炸弹。求引爆所有炸弹的最小代价。

    分析

    先做(n^2)的循环,然后建图,对(i)能引爆(j)建边((i,j))。然后对这个图求强连通分量并缩点,构成新的有向无环的森林。定义每个强连通分量的cost为其中包含的点的最小cost,然后把新森林中所有入度为0的点的cost加起来求和即可(由于无环,所以从任何入度不为0的点往回走,必然终止于一个入度为0的点)。

    代码

    #include <iostream>
    #include <cstring>
    #include <cstdlib>
    #include <cmath>
    #include <algorithm>
    #include <vector>
    #include <set>
    #include <map>
    #include <queue>
    #include <stack>
    #define MP make_pair
    #define PB push_back
    #define fi first
    #define se second
    #define ZERO(x) memset((x), 0, sizeof(x))
    #define ALL(x) (x).begin(),(x).end()
    #define rep(i, a, b) for (int i = (a); i <= (b); ++i)
    #define per(i, a, b) for (int i = (a); i >= (b); --i)
    #define QUICKIO                  
        ios::sync_with_stdio(false); 
        cin.tie(0);                  
        cout.tie(0);
    #define MS(x,y) memset(x,y,sizeof(x))
    #define int ll
    using namespace std;
    typedef long long ll;
    
    const int MAXN=1005;
    vector<int> G[MAXN];
    bool mat[MAXN][MAXN];
    int n;
    int pre[MAXN], lowlink[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
    stack<int> stk;
    
    void dfs(int u)
    {
    	pre[u]=lowlink[u] = ++dfs_clock;
    	stk.push(u);
    	rep(i,0,n-1)
    	{
    		if(!mat[u][i]) continue; 
    		int v=i;
    		if(!pre[v])
    		{
    			dfs(v);
    			lowlink[u]=min(lowlink[u],lowlink[v]);
    		}
    		else if(!sccno[v])
    		{
    			lowlink[u]=min(lowlink[u],pre[v]);
    		}
    	}
    	if(lowlink[u]==pre[u])
    	{
    		scc_cnt++;
    		for(;;)
    		{
    			int x=stk.top(); stk.pop();
    			sccno[x]=scc_cnt;
    			if(x==u) break;
    		}
    	}
    }
    
    void find_scc()
    {
    	dfs_clock=scc_cnt=0;
    	ZERO(sccno);
    	ZERO(pre);
    	rep(i,0,n-1)
    		if(!pre[i]) dfs(i);
    }
    
    bool nmat[MAXN][MAXN];
    
    vector<pair<int,int> > edges;
    vector<int> nG[MAXN];
    int ncnt=0;
    
    void add_edges(int u,int v)
    {
    	edges.PB(MP(u,v));
    	nG[u].PB(edges.size()-1);
    }
    
    pair<int,int> pnt[MAXN];
    int pntc[MAXN], pntr[MAXN];
    
    inline double dist(int x,int y)
    {
    	return sqrt((pnt[x].fi-pnt[y].fi)*(pnt[x].fi-pnt[y].fi)+
    				(pnt[x].se-pnt[y].se)*(pnt[x].se-pnt[y].se));
    }
    int cost[MAXN];
    signed main()
    {
    	int T; scanf("%lld", &T);
    	rep(kase,1,T)
    	{
    		ZERO(nmat);
    		ZERO(mat);
    		scanf("%lld", &n);
    		rep(i,1,n)
    		{
    			int x,y;
    			scanf("%lld%lld%lld%lld", &x,&y, &pntr[i], &pntc[i]);
    			pnt[i]=MP(x,y);
    		}
    		rep(i,1,n)
    		{
    			rep(j,1,n)
    			{
    				if(i==j) continue;
    				double d=pntr[i]-dist(i,j);
    				if(fabs(d)<1e-6 || d>1e-6)
    				{
    					mat[i-1][j-1]=true;
    				}
    			}
    		}
    		/*
    		rep(i,0,n-1)
    		{
    			rep(j,0,n-1)
    				cout<<mat[i][j]<<" ";
    			cout<<endl;
    		}
    		*/
    		find_scc();
    		memset(cost,0x3f,sizeof(cost));
    		rep(i,1,n)
    		{
    			cost[sccno[i-1]]=min(cost[sccno[i-1]],pntc[i]);
    		}
    		rep(i,0,n-1)
    		{
    			rep(j,0,n-1)
    			{
    				if(i==j) continue;
    				nmat[sccno[i]][sccno[j]]|=
    					mat[i][j];
    			}
    		}
    		/*
    		rep(i,1,scc_cnt)
    		{
    			rep(j,1,scc_cnt)
    				cout<<nmat[i][j]<<" ";
    			cout<<endl;
    		}
    		*/
    		ll ans=0;
    		/*
    		rep(i,0,n-1) cout<<sccno[i]<<" ";
    		cout<<endl;
    		rep(i,0,n-1) cout<<cost[i]<<" ";
    		cout<<endl;
    		*/
    		
    		rep(i,1,scc_cnt)
    		{
    			int ok=0;
    			rep(j,1,scc_cnt)
    			{
    				if(i==j) continue;
    				if(nmat[j][i])
    				{
    					ok++;
    					break;
    				}
    			}
    			if(!ok)
    			{
    				//cout<<i<<" "<<cost[i]<<endl;
    				ans+=cost[i];
    			}
    		}
    		printf("Case #%lld: %lld
    ", kase, ans);
    	}
    	return 0;
    }
    

    札记

    这题是在一场训练赛中打的。当时的我们激情卡题两个半小时23333然后我觉得不行了只能换题,不懂图论的队友说了这题可以写,他觉得是带权并查集23333我想了一下,这一看就是缩点啊。然后缩点之后没什么好办法,不过也没浪费时间——他们还在卡题23333过了又是半个小时,他们终于出了另外一题(卡的那个签到题还是没出!!!),这个时候还剩下一个半小时了,我想到可以求和入度为0的点即可。然后又过去半个小时(出签到题啊啊啊啊啊)没出(- -|||),只好我上写这题,然后半个小时写完,5分钟调试,交上去WA,看了下代码,改了个long long,过了。后来那个签到题成功出了(太真实了),我们翻盘大成功,哇咔咔~

    如非注明,原创内容遵循GFDLv1.3发布;其中的代码遵循GPLv3发布。
  • 相关阅读:
    C#高级编程第11版
    做点字符串题
    Codeforces Round #681 (Div. 1, based on VK Cup 2019-2020
    Educational Codeforces Round 97 题解
    AtCoder Regular Contest 106 题解
    Kick Start Round G 2020 题解
    CCSP 2020题解
    Codeforces Round #675 (Div. 2) 题解
    AtCoder Regular Contest 104
    Kick Start Round F 2020 题解
  • 原文地址:https://www.cnblogs.com/samhx/p/HDU-5934.html
Copyright © 2020-2023  润新知