P2446 [SDOI2010]大陆争霸

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P2446 [SDOI2010]大陆争霸相关的知识,希望对你有一定的参考价值。

P2446 [SDOI2010]大陆争霸

题意:

n个点,m个边,wi为每个边的边权,对于每个点i,其被l个点保护着,也就是如果保护其的点没有被破坏,点i无法被破坏(也无法经过其前往其他点)。现在从1出兵(无限数量),问破坏点n的最短时间

题解:

很明显这个题跟最短路有关,对于每个点,有这几种状态:是否保护,是否已经到达
因为如果我们跑最短路,跑出来的结果并不是,因为有些点受保护的情况,实际到达时间要被推迟(直到保护他的点也被破坏)
我们用now[u]表示点u被破坏的时间,arrive[u]表示u无人保护的时刻(如果一开始就无人保护,默认值为0),dis[u]表示路径上到达u的时间
对于now[u]应该取dis[u]和arrive[u]的最大值,因为u被破坏需要可达(不被保护)且已经到达(路径距离)
dis[u]最短路维护,arrive[u]怎么维护呢?
arrive[u]记录的其实是保护u的点x,x被破坏的最大值。所以如果u被x保护,我们就从x到u建一个边(无向边),在跑最短路过程中,不断更新arrive[u],arrive[u]=max(now[x])
详细可以见代码

代码:

#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\\n",a,b);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll=1e18;
const int INF_int=0x3f3f3f3f;
inline ll read(){
   ll s=0,w=1ll;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1ll;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10ll+((ch-'0')*1ll),ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);
   return s*w;
}
void rd_test(){
	#ifdef ONLINE_JUDGE
	#else
		startTime = clock(); //计时开始
        freopen("landcraft.in","r",stdin);
	#endif
}
void Time_test(){
	#ifdef ONLINE_JUDGE
	#else
		endTime = clock(); //计时结束
   		printf("\\n运行时间为:%lfs\\n",(double)(endTime - startTime) / CLOCKS_PER_SEC);
	#endif
}
const int maxn=1e6+9;
struct Edge
{
    int u,v,w,next;
}e[maxn];
int head[maxn],cnt,n,m,s,vis[maxn],dis[maxn],pre[maxn];
int ind[maxn],arrive[maxn],now[maxn];
struct node
{
    int w,now;
    inline bool operator <(const node &x)const
    //重载运算符把最小的元素放在堆顶(大根堆)
    {
        return w>x.w;//这里注意符号要为'>'
    }
};
priority_queue<node>q;
inline void add(int u,int v,int w)
{
    e[++cnt].u=u;
    //这句话对于此题不需要,但在缩点之类的问题还是有用的
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    //存储该点的下一条边
    head[u]=cnt;
    //更新目前该点的最后一条边(就是这一条边)
}
vector<int>vec[maxn];
void dijkstra()
{
    for(int i=1;i<=n;i++)
    {
        dis[i]=INF_int;
        arrive[i]=0;
    }
    dis[s]=arrive[s]=0;
    //赋初值
    q.push((node){0,s});
    int u,v;
    while(!q.empty())
    //堆为空即为所有点都更新
    {
        node x=q.top();
        q.pop();
        u=x.now;
        //记录堆顶(堆内最小的边)并将其弹出
        if(vis[u]) continue; 
        //没有遍历过才需要遍历
        vis[u]=1;
        
		now[u]=max(dis[u],arrive[u]);//到达u的时间时间 
        
        for(int i=head[u];i;i=e[i].next)
        //更新所有dis[v] 
        {
            v=e[i].v;
            if(dis[v]>now[u]+e[i].w)
            {
            	
            	dis[v]=now[u]+e[i].w;
            	if(!ind[v]){//如果这个点没有被保护 
            	    pre[v]=u;
            	    //松弛操作
            	    now[v]=max(dis[v],arrive[v]);
            	    q.push((node){max(dis[v],arrive[v]),v});
            	    //把新遍历到的点加入堆中
				}
                
            }
        }
        //开始摧毁点u 
        for(int i=0;i<vec[u].size();i++){
        	v=vec[u][i];
        	ind[v]--;
        	arrive[v]=max(arrive[v],now[u]);
        	if(!ind[v]){
        		now[v]=max(dis[v],arrive[v]);
				q.push((node){now[v],v}); 
			}
		}
    }
}

int main()
{
	rd_test();
	
	cin>>n>>m;s=1; 
	for(int i=1;i<=m;i++){
		int x,y,z;
		x=read(),y=read(),z=read();
        add(x,y,z);
	}
	
	
	for(int i=1;i<=n;i++){
		int l=read();
		if(l==0)continue;
		for(int j=1;j<=l;j++){
			int x=read(); 
			++ind[i];
			vec[x].push_back(i);
			//vec[i].push_back(x);
			//add(x,i,0);
		}
	}
	dijkstra();
	cout<<now[n];
	//Time_test();
}




以上是关于P2446 [SDOI2010]大陆争霸的主要内容,如果未能解决你的问题,请参考以下文章

AC日记——[SDOI2010]大陆争霸 洛谷 P3690

BZOJ 1922: [Sdoi2010]大陆争霸

bzoj 1922: [Sdoi2010]大陆争霸

[SDOI2010]大陆争霸

bzoj 1922 [Sdoi2010]大陆争霸(最短路变形)

题解 [SDOI2010] 大陆争霸