zoukankan      html  css  js  c++  java
  • Codeforces 500 E. New Year Domino

    [$>Codeforces space 500 E. New Year Domino<$](http://codeforces.com/contest/500/problem/E)

    题目大意 : 数轴上有序排列着 (n) 块多米诺骨牌, 第 (i) 块骨牌坐标为 (p_i) 长度为 (l_i)
    如果推倒坐标为 (p_i) 的多米诺骨牌,那么区间 ([p_i, p_i + x_i]) 中的多米诺骨牌都会被推倒,从而发生连锁反应.
    现在有 (q) 组询问,每次询问之前可以让一些多米诺骨牌增加任意长度,代价是增加的长度之和。
    询问的内容是每次推倒第 (x) 块骨牌,求要推倒第 (y) 块至少要花费的代价 (询问之间相互独立)

    http://codeforces.com/predownloaded/0e/cc/0eccc111daae4ba96116471d865839d66839763d.png

    (2 leq n , qleq 2 imes 10^5 1 leq p_i, l_i leq 10^9)

    解题思路 :

    观察发现,多米诺之间的连锁反应可以合并成一个块,每次从右到左每次新加骨牌的时候可以合并被它覆盖的块。

    但是直接按照定义合并的话,只能保证一个块最左边的块可以推倒整个块,也就是说一个块的贡献只有在推倒整块最左边的骨牌时是合法的

    仔细想了很久觉得并没有什么做法可以在线求出 (x) 对应的合法联通块,不妨大胆离线.

    离线后按照询问的左端点排序,从右到左扫,边扫边用一个栈维护此时的联通块的状态和联通块之间的距离

    对于每一个扫到的 (i),在其合并完之后处理 (i) 作为左端点的询问的答案

    因为此时 (i) 左边的点还没有被加进联通块,所以从 (i) 开始推一定是合法的, 对于每一个左端点在 (i) 上的询问,算出两个联通块之间的距离就是答案


    /*program by mangoyang*/
    #include<bits/stdc++.h>
    #define inf (0x7f7f7f7f)
    #define Max(a, b) ((a) > (b) ? (a) : (b))
    #define Min(a, b) ((a) < (b) ? (a) : (b))
    typedef long long ll;
    using namespace std;
    template <class T>
    inline void read(T &x){
        int f = 0, ch = 0; x = 0;
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
        for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
        if(f) x = -x;
    }
    #define int ll
    #define N (1000005)
    int fa[N], l[N], r[N], st[N], sum[N], Ans[N], n, m, top;
    struct Query{ int x, id; }; vector<Query> q[N];
    inline int ask(int x){ return x == fa[x] ? x : fa[x] = ask(fa[x]); } 
    main(){
    	read(n);
    	for(int i = 1, x, y; i <= n; i++)
    		read(x), read(y), l[i] = x, r[i] = x + y;
    	for(int i = 1; i <= n; i++) fa[i] = i;
    	read(m);
    	for(int i = 1, L, R; i <= m; i++)
    		read(L), read(R), q[L].push_back((Query){R, i});
    	for(int i = n; i >= 1; i--){
    		int x = i;
    		while(top && l[st[top]] <= r[x]){
    			r[x] = Max(r[x], r[st[top]]);
    			fa[ask(st[top])] = x, top--;
    		}
    		if(top) sum[x] = sum[st[top]] + l[st[top]] - r[x];
    		st[++top] = x;
    		for(int j = 0; j < q[i].size(); j++){
    			int id = q[i][j].id, y = q[i][j].x;
    			Ans[id] = sum[ask(x)] - sum[ask(y)];
    		}
    	}
    	for(int i = 1; i <= m; i++) printf("%lld
    ", Ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    简单的多重背包
    完美子图
    活动投票
    人品问题
    售票系统
    最短路径
    优美值
    前端-常用函数记录-持续更新
    前端-单点登录中cookie中domain的思考
    大白话说GIT常用操作,常用指令git操作大全
  • 原文地址:https://www.cnblogs.com/mangoyang/p/9325903.html
Copyright © 2011-2022 走看看