• 【XSY2434】【CF787D】遗产(线段树+dijkstra)


    (Description)

    (Rick)和他的同事们研究出了一种新的有关放射的公式,于是许多坏人就在追赶他们。所以(Rick)希望在被坏人抓住之前把遗产给(Morty)

    在他们的宇宙里总共有(n)颗行星,每颗行星有它自己的编号(编号为(1)(n))。(Rick)所在的行星的编号是(s)(地球),但是他不知道(Morty)在哪?总所周知,(Rick)有一门能打开奇妙入口的枪。在这把枪的帮助下,他能打开一扇单向门去往任意一个星球(包括那把枪自己所在的星球),但是这玩意是有限制的,因为(Rick)用的是这玩意的免费试用版。

    一般而言,他不能用这把枪打开任意一扇单向的门。但是有(q)个套餐在它的官网上售卖。每一次你购买了这个套餐,你就能也仅仅能使用它一次,但是你可以重复购买(如果你觉得需要多次使用的话)。

    网站上的套餐有以下三种类型:

    1.打开一扇从(v)(u)的门

    2.打开一扇从(v)([l,r])之间任何一个的门

    3.打开一扇从([l,r])(v)之间任何一个的门

    (Rick)不知道(Morty)在哪?但是(Unity)准备告诉他。于是(Rick)就要准备好一切。

    因为(Rick)的预算不多,所以他想知道从他的星球出发,到达每一个星球的最少花费是多少,如果到达不了,就输出(-1).


    (Input)

    第一行输入包括三个正整数(n,q,s(1<=n,q<=10^5,1<=s<=n))(n)表示行星的个数,(q)表示计划的总数,(s)表示地球的编号。

    接下来(q)行每行包括一个计划。每一行的第一个数字为(t(1<=t<=3)),表示该计划的类型。如果(t=1)就跟着输出(v,u)(w)(w)表示这个计划的花费。其他的就输入(v,l,r)(w)((1<=v,u<=n,1<=l<=r<=n,1<=w<=10^9))


    (Output)

    输出一行包括(n)个数,(a_{i})表示从地球到(i)这个星球的最小花费,如果到达不了则输出(-1)


    (Sample) (Input)

    样例输入1:

    3 5 1
    2 3 2 3 17
    2 3 2 2 16
    2 2 2 3 3
    3 3 1 1 12
    1 3 3 17

    样例输入2:

    4 3 1
    3 4 1 3 12
    2 2 3 4 10
    1 2 4 16


    (Sample) (Output)

    样例输出1:

    0 28 12

    样例输出2:

    0 -1 -1 12


    (Source)

    练习题 树4-1-线段树


    思路

    首先,这道题可以很容易看出是一道最短路问题

    我们首先想到就是对于(v)每一个([l,r])区间内的数进行连边

    但这样的连边的最坏复杂度都可以达到(O(nq)),极其容易被卡死

    所以我们再仔细观察题目,不去考虑最短路的话。。。

    一堆区间操作是不是很像线段树?!

    于是我们使用线段树优化最短路

    我们考虑对于情况2,3分别建一棵线段树,为入树和出树

    其中线段树的每一个节点都表示一个区间

    对于操作2,我们就可以将图的节点与入树上区间所对应的节点连边

    对于操作3,我们就可以将出树上区间所对应的节点与图的节点连边

    我们还需要在入树和出树他们各自自己树内建边,当然权值为(0)

    最后跑一遍最短路(dijkstra)就可以了


    代码

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=700050,M=3500010l;
    const ll INF=0x7f7f7f7f7f7f7f7f;
    int n,t,s,cnt=0;
    int head[N];
    int lc1[N],lc2[N],rc1[N],rc2[N];
    //lc1[now],rc1[now]分别表示在入树内now的左右儿子,lc2,rc2是表示在出树内
    int to[M<<1],nxt[M<<1];
    int val[M<<1];
    int nodetot=0;//最短路内的节点数
    ll dis[N];
    bool vis[N];
    struct edge
    {
    	int u;
    	ll cost;
    	bool operator < (const edge & e)const
    	{
    		return cost>e.cost;
    	}
    }now;
    priority_queue<edge>q;
    void add(int u,int v,int w)
    {
    	to[++cnt]=v;
    	nxt[cnt]=head[u];
    	val[cnt]=w;
    	head[u]=cnt;
    }
    int build1(int l,int r)
    {
        if(l==r)return l;
        int now=++nodetot;
        int mid=(l+r)>>1;
        lc1[now-n]=build1(l,mid);
        add(now,lc1[now-n],0);//父亲与儿子连入边,父亲->儿子
        rc1[now-n]=build1(mid+1,r);
        add(now,rc1[now-n],0);
        return now;
    }
    int build2(int l,int r)
    {
        if(l==r)return l;
        int now=++nodetot;
        int mid=(l+r)>>1;
        lc2[now-n]=build2(l,mid);
        add(lc2[now-n],now,0);//父亲与儿子连出边,儿子->父亲
        rc2[now-n]=build2(mid+1,r);
        add(rc2[now-n],now,0);
        return now;
    }
    void modify1(int v,int l,int r,int w,int ql,int qr,int k)
    {
    	if(l<=ql&&qr<=r)
    	{
    		add(v,k,w);//到达区间内,将图内的点与这个区间对应的节点连边
    		return ;
    	}
    	int mid=(ql+qr)>>1;
    	if(l<=mid)modify1(v,l,r,w,ql,mid,lc1[k-n]);
            if(r>mid)modify1(v,l,r,w,mid+1,qr,rc1[k-n]);
    }
    void modify2(int v,int l,int r,int w,int ql,int qr,int k)
    {
    	if(l<=ql&&qr<=r)
    	{
    		add(k,v,w);
    		return ;
    	}
    	int mid=(ql+qr)>>1;
    	if(l<=mid)modify2(v,l,r,w,ql,mid,lc2[k-n]);
    	if(r>mid)modify2(v,l,r,w,mid+1,qr,rc2[k-n]);
    }
    void dijkstra(int st)
    {
    	dis[st]=0;
    	q.push((edge){st,0});
    	while(!q.empty())
    	{
    		now=q.top();
    		q.pop();
    		if(vis[now.u])continue;
    		vis[now.u]=1;
    		dis[now.u]=now.cost;
    		for(int i=head[now.u];i;i=nxt[i])
    		{
    			if(!vis[to[i]]&&(now.cost+(ll)val[i])<dis[to[i]])
    			{
    				q.push((edge){to[i],now.cost+(ll)val[i]});
    			}
    		}
    	}
    }
    int main()
    {
    	scanf("%d %d %d",&n,&t,&s);
    	memset(dis,0x7f,sizeof(dis));
    	nodetot=n;
    	int rt1=build1(1,n);
    	int rt2=build2(1,n);
    	int op,v,u,l,r,w;
    	for(int i=1;i<=t;i++)
    	{
    		scanf("%d",&op);
    		if(op==1)
    		{
    			scanf("%d %d %d",&v,&u,&w);
    			add(v,u,w);//直接连边
    		}
    		if(op==2)
    		{
    			scanf("%d %d %d %d",&v,&l,&r,&w);
    			modify1(v,l,r,w,1,n,rt1);
    		}
    		if(op==3)
    		{
    			scanf("%d %d %d %d",&v,&l,&r,&w);
    			modify2(v,l,r,w,1,n,rt2);
    		}
    	}
    	dijkstra(s);
    	for(int i=1;i<=n;i++)
    	{
    		if(dis[i]==INF)printf("-1 ");
    		else printf("%lld ",dis[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    窗口切换快捷键
    简单排序算法
    (转)Zen Coding 让 Notepad++ 代码书写健步如飞
    (转)图解SQL的Join
    第6章 AJAX
    今天是五四青年节
    看到的php的小知识
    (转)Notepad++配合QuickText打造快速高效的文本编辑器
    一种非常好的产生"唯一"临时文件的办法
    (转)linux 目录结构及其含义
  • 原文地址:https://www.cnblogs.com/ShuraEye/p/11622477.html
Copyright © 2020-2023  润新知