• [CERC2017]Buffalo Barricades(思维/传递闭图/并查集)


    Problem

    codeforces 题目地址

    洛谷 题目地址

    Solution

    首先考虑将每个奶牛归入到最后划分出来的区域,忽略掉一个奶牛可能重复累计到多个区间的条件,例如:

    其中奶牛 (1,2) 归给操作 ('A'),奶牛 (3) 归给操作 ('B'),以此类推。

    扫描线来实现这一步。我们从右往左扫描每个点,用一个 (set) 维护扫描过的栅栏点的 (y) 值,且 (set) 中维护的栅栏点仍在向左延伸。具体的:

    • 如果扫描到一个栅栏点 (p),将其插入到 (set),再一直往下走,如果遇到了比他时间大的点 (q),将 (q)(set) 中删除,直到遇到第一个比他时间小的点 (t) break。(即第一个 (y_t<y_p,t<p)(p,t)是点的编号,直接代表时间)

    • 如果扫描到一个奶牛点 (p),将其归到 (set)(y) 值比他大的第一个点 (t) 内。

    记编号为 (i) 的栅栏点经过上述算法统计出来的区间奶牛数为 (sz_i)

    我们称包含关系为:区间 (i) 包含区间 (j)(即 (x_j<x_i,y_j<x_i)),且 (i) 的时间比 (j) 小(即 (i < j)),则 (j)(i) 最后的答案有 (sz_j) 贡献。

    有了这个关系,我们可以对于每个区间对,如果满足包含关系,则可以 (j->i) 建一条边。于是我们得到了一张 (DAG),但是边的数量是 (n^2) 的,考虑优化。

    可以发现,如果 (i->j->k),则 (i->k),这种关系又叫做传递闭图。可以从这里入手,不建这么多边。具体的: 每个栅栏点 (j) 向右上角离它最近的一个点 (i) 建一条边,这样建满足了第一维关系((x_j<x_i,y_j<x_i))。记栅栏点 (i) 连向的右上角离它最近的栅栏点为 (pa[i])。这样建出来的图是一个内向森林。

    在这个图中,如果 (i)(j) 的祖先,且 (j -> i) 路径上所有的时间都比 (i) 大,则可以 (j)(i) 造成贡献。这个可以按时间从大到小枚举 (i),加入 (i -> pa[i]) 这条边。这样扫到 (j) 时,(j) 的联通块内所有的点的时间都比 (j) 大,这样 (j) 联通块内所有点 (k)(sz_k) 都可以对 (j) 造成贡献。

    时间复杂度 (O(n log n))

    Code

    Talk is cheap.Show me the code.

    #include<bits/stdc++.h>
    #define pb push_back
    #define INF 0x3f3f3f3f
    using namespace std;
    inline int read() {
        int x = 0, f = 1; char ch = getchar();
        while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
        return x * f;
    }
    const int N = 1e6+7;
    int np,n;
    int Y[N<<1],sz[N],fa[N],pa[N],ans[N];
    struct Node {
    	int x,id;
    	bool operator < (const Node &el) const {
    		return (x==el.x ? id>el.id : x>el.x);
    	}
    }p[N<<1];
    int Find(int x) {
    	return (fa[x]==x ? x : fa[x]=Find(fa[x]));
    }
    int main()
    {
    	//freopen("")
    	np = read();
    	for(int i=1;i<=np;++i) {
    		int x = read();
    		Y[i] = read();
    		p[i] = (Node)<%x,i%>;
    	}
    	n = read();
    	for(int i=np+1;i<=np+n;++i) {
    		int x = read();
    		Y[i] = read();
    		p[i] = (Node)<%x,i%>;
    	}
    	sort(p+1, p+1+np+n); //如果x相同 操作需排在点前面 
    	memset(pa, -1, sizeof(pa));
    	set<Node> s;
    	for(int i=1;i<=np+n;++i) {
    		int id = p[i].id, y = Y[id];
    		if(id <= np) {
    			//printf("# %d
    ",id);
    			set<Node>::iterator it = s.lower_bound((Node)<%-y,INF%>);
    			if(it != s.end()) {
    				Node tmp = *it;
    				int tid = tmp.id-np;
    				++sz[tid];
    				//printf("star %d calc %c
    ",id,tid+'A'-1);
    			}
    		} else {
    			//printf("# %c
    ",id-np+'A'-1);
    			set<Node>::iterator it = s.lower_bound((Node)<%-y,INF%>);
    			if(it!=s.end()) pa[id-np] = it->id-np;
    			s.insert((Node)<%-y,id%>);
    			it = s.find((Node)<%-y,id%>);
    			if(it != s.begin()) --it;	//注意 
    			vector<Node> vec;
    			for(;it!=s.begin();--it) {
    				if(it->id < id) break;
    				vec.pb(*it);
    			}
    			if(it->id > id) vec.pb(*it);
    			for(int i=0;i<vec.size();++i) {
    				s.erase(vec[i]);
    			}
    			vec.clear();
    		}
    	}
    	for(int i=1;i<=n;++i) fa[i] = i;
    	for(int i=n;i>=1;--i) {
    		int fx = Find(i);
    		ans[i] = sz[fx];
    		if(pa[i] != -1) {
    			int fy = Find(pa[i]);
    			fa[fx] = fy; sz[fy] += sz[fx];
    		}
    	}
    	for(int i=1;i<=n;++i) printf("%d
    ",ans[i]);
    	return 0;
    }
    /*
    7
    1 1
    4 2
    6 2
    5 3
    2 5
    4 7
    7 5
    4
    4 4
    8 2
    9 6
    6 5
     
    2
    1
    3
    2
    */
    

    Summary

    注意 (set) 的使用!

  • 相关阅读:
    RxJava+okhttp3
    RetrofitOkHttp网络请求
    布局111
    网络请求展示数据
    一级列表展示购物车
    终极MVP二级购物车
    将博客搬至CSDN
    nyoj-开灯问题
    nyoj-数乌龟
    nyoj 正三角形的外接圆面积
  • 原文地址:https://www.cnblogs.com/BaseAI/p/14050651.html
Copyright © 2020-2023  润新知