zoukankan      html  css  js  c++  java
  • [JOISC2020] 治療計画

    前言

    也许是人生第一道线段树优化建图。

    也许不是。

    题目

    洛谷

    LibreOJ

    讲解

    警告!无图!

    想象一条线段表示国民,我们需要用治疗方案(线段)覆盖这些国民,但是这些国民都会每秒被相邻的感染者感染,为了不使之前的国民再次被感染,我们两次治疗方案一定要能够衔接起来。

    接下来就是开脑洞的时候了,我们抛开时间这一维不看,单纯只用治疗方案覆盖这些国民,我们发现一定是从 (1) 号国民覆盖到 (n) 号国民。

    而两次治疗方法 (i,j) ( (i) 先于 (j) 实施) 能够衔接的条件为:

    [r_i-l_jge |t_i-t_j| ]

    其中 (l,r) 表示治疗方案的左右断电,(t) 表示实施治疗方法时间。

    如果我们把绝对值拆开,那么就有两种情况:

    • (t_i > t_j,r_i-t_ige l_j-t_j.)
    • (t_i<t_j,r_i+t_ige l_j+t_j.)

    取等的情况时候随便放到一边就行。

    于是我们可以把治疗方案看做节点,如果一对有序治疗方案满足上述条件,那么连边,此时我们可以将边权放在终点,也就是把边权转换为点权,然后跑最短路。

    由于点权的特殊性,我们只需要目标位置 (j) 的前驱 (i) 权值最小即可贪心松弛,跑 ( t dijkstra) 就行。

    而且每条边一定只会松弛一次,这为复杂度提供保障。

    什么?你问我怎么找满足条件的边?可以发现上面两种情况的不等式 (i,j) 独立,分情况建两棵线段树,然后暴力找就行了。因为松弛一次,所以每次找到了之后直接把权值赋为 (+infty),表示以后不会再来就行。

    时间复杂度 (O(mlog_2m))

    代码

    //12252024832524
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std; 
    
    typedef long long LL;
    const int MAXM = 100005;
    const int INF = 0x3f3f3f3f << 1;
    int n,m,POS;
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    LL dis[MAXM];
    struct Cure
    {
    	int t,l,r,val;
    	void Get(){t = Read(); l = Read(); r = Read(); val = Read();}
    	bool operator < (const Cure &px)const{
    		return t < px.t;
    	}
    }c[MAXM];
    
    struct node
    {
    	int x; LL w;
    	node(){}
    	node(int x1,LL w1){
    		x = x1;
    		w = w1;
    	}
    	bool operator < (const node &px)const{
    		return w > px.w;
    	}
    };
    priority_queue<node> q;
    
    #define lc (x<<1)
    #define rc (x<<1|1)
    struct SegmentTree
    {
    	int MIN[MAXM << 2];
    	
    	void up(int x){MIN[x] = Min(MIN[lc],MIN[rc]);}
    	
    	void Build(int x,int l,int r,int v)
    	{
    		if(l == r) {MIN[x] = c[l].l + v*c[l].t;return;}
    		int mid = l+r >> 1;
    		Build(lc,l,mid,v); Build(rc,mid+1,r,v);
    		up(x);
    	}
    }st[2];
    void Del(int x,int l,int r,int pos)
    {
    	if(l == r)
    	{
    		st[0].MIN[x] = st[1].MIN[x] = INF;
    		return;
    	}
    	int mid = l+r >> 1;
    	if(pos <= mid) Del(lc,l,mid,pos);
    	else Del(rc,mid+1,r,pos);
    	st[0].up(x); st[1].up(x);
    }
    void Solve(int opt,int x,int l,int r,int ql,int qr,int v)
    {
    	if(st[opt].MIN[x] > v) return;
    	if(l == r)
    	{
    		q.push(node(l,dis[l] = dis[POS] + c[l].val));
    		st[0].MIN[x] = st[1].MIN[x] = INF;
    		return;
    	}
    	int mid = (l+r) >> 1;
    	if(ql <= mid) Solve(opt,lc,l,mid,ql,qr,v);
    	if(mid+1 <= qr) Solve(opt,rc,mid+1,r,ql,qr,v);
    	st[0].up(x); st[1].up(x);
    } 
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= m;++ i) c[i].Get();
    	sort(c+1,c+m+1); memset(dis,0x3f,sizeof(dis));
    	st[0].Build(1,1,m,-1); st[1].Build(1,1,m,1);
    	for(int i = 1;i <= m;++ i)
    		if(c[i].l == 1)
    		{
    			q.push(node(i,dis[i] = c[i].val));
    			Del(1,1,m,i);
    		}
    	while(!q.empty())
    	{
    		node t = q.top(); q.pop();
    		POS = t.x;
    		if(c[POS].r == n){Put(dis[POS]);return 0;}
    		if(POS ^ 1) Solve(0,1,1,m,1,POS-1,c[POS].r - c[POS].t + 1);
    		if(POS ^ m) Solve(1,1,1,m,POS+1,m,c[POS].r + c[POS].t + 1);
    	} 
    	Put(-1);
    	return 0;
    }
    /*
    c[i].r - c[j].l >= Abs(c[i].t-c[j].t)
    c[i].t > c[j].t
    	c[i].r - c[i].t >= c[j].l - c[j].t
    c[i].t < c[j].t
    	c[i].r + c[i].t >= c[j].l + c[j].t
    */
    
  • 相关阅读:
    ImageIO 操作图片
    ThreadLocal
    Mysql表字段命令alter add
    php中的for循环和js中的for循环
    获取当前URL地址和$_GET获取参数
    apache中的vhosts的配置。
    PHP获取前台传过来的时间年份,进行处理。
    php中的htmlspecialchars_decode()函数
    php分页类代码和使用
    cookies插件,记住cookies
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15022704.html
Copyright © 2011-2022 走看看