zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

    神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。

    input
    输入数据的第一行是三个整数n,m,T。
    第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。

    output
    输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。

    sample input
    3 3 3
    1 2 0 2
    2 3 0 3
    1 3 1 2
    sample output
    Yes
    No
    Yes

    样例说明:
    0时刻,出现两条边1-2和2-3。
    第1时间段内,这个图是二分图,输出Yes。
    1时刻,出现一条边1-3。
    第2时间段内,这个图不是二分图,输出No。
    2时刻,1-2和1-3两条边消失。
    第3时间段内,只有一条边2-3,这个图是二分图,输出Yes。

    数据范围:
    n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。

    @solution@

    有在线 LCT 的做法和离线分治的做法,都很经典。
    出于某些原因,我只写了分治的做法。
    LCT 的后面来慢慢补(咕咕咕)

    假如这个题只存在加边而不存在删边,我们应该怎么动态维护当前图是否为二分图?
    二分图的定义是存在一个二染色的方案,我们不妨将当前的二染色方案维护出来。
    当新加入一条边的两个端点在同一连通块时,查询它们的颜色是否相同,相同则不是二分图。
    否则,我们需要合并两个连通块,可能还需要将某个连通块的颜色全部取反以保证它是二分图。

    我们需要一个数据结构合并连通块,并且在合并的时候做整个连通块的修改。
    并查集。可以在集合的代表元素处打 tag 再进行合并,访问某点时就可以将该点到集合代表元素(也就是根)的 tag 合并起来。

    那么现在出现了删除操作,显然并查集上搞删除。。。那不就是 lct 干的事情吗。
    但同时,我们也可以离线对时间分治回避删除的问题。
    (其实我也不知道这算不算 cdq 分治。。。网上有说 cdq 分治必须要左区间对右区间影响,又有人说 cdq 分治其实就是时间分治。。。)

    一条边存在的时间在时间轴上对应一个区间 (start, end]。为了方便,以下简写为 (s, e]
    假如当前我们已经递归到时间轴上 [l, r],如果一条边的 (s, e] 完全包含 [l, r],则在此刻在并查集中加入该边。
    判断此刻是否为二分图,如果不是,显然加入更多的边也不为二分图,直接将 [l, r] 中的答案改为 No。
    否则继续往下递归。取中点 mid = (l + r)/2,分为左右两个小区间。将 [l, r] 没有处理的边如果跟左右区间有交集就递归传到左右两个区间,
    如果递归到底,则可以判断该时刻的答案为 Yes。

    该区间处理完毕,回撤并查集中在该区间 [l, r] 加入的边(注意并查集不能删除,但是能回撤)。
    注意我们要回撤并查集信息,所以要写按秩合并的并查集。

    事实上这样的递归结构就类似于线段树,每一条边的 (s, e] 被拆成线段树上 log n 个点,线段树的底部结点就是时间轴的每一时刻。
    我们分治的过程相当于自上而下遍历该线段树,将底部结点到线段树的根上所有信息合并处理出该时刻对应的图,进行判断。遍历返回时将结点信息回撤。
    直接改 No 理性理解一下,大概就是在剪枝。

    具体。。。看代码?

    @accepted code@

    #include<cstdio>
    #include<vector>
    #include<algorithm>
    using namespace std;
    const int MAXN = 100000 + 5;
    const int MAXM = 200000 + 5;
    const int MAXT = 100000 + 5;
    struct edge{
    	int u, v, s, e;
    	edge(int _u=0, int _v=0, int _s=0, int _e=0):u(_u), v(_v), s(_s), e(_e){}
    };
    int fa[MAXN], rnk[MAXN], d[MAXN];
    int find(int x) {
    	return (fa[x] == x) ? x : find(fa[x]);
    }
    int dis(int x) {
    	return (fa[x] == x) ? 0 : d[x]^dis(fa[x]);
    }
    struct modify{
    	int x, y; bool b;
    }stk[MAXN];
    int tp = 0;
    bool unite(int x, int y) {
    	int fx = find(x), fy = find(y);
    	if( fx == fy ) return dis(x)^dis(y);
    	else {
    		if( rnk[fx] > rnk[fy] ) swap(fx, fy);
    		d[fx] = dis(x)^dis(y)^1;
    		tp++; stk[tp].x = fx; stk[tp].y = fy;
    		if( rnk[fx] == rnk[fy] ) rnk[fy]++, stk[tp].b = true;
    		else stk[tp].b = false;
    		fa[fx] = fy; 
    		return true;
    	}
    }
    void restore(int x) {
    	while( tp != x ) {
    		fa[stk[tp].x] = stk[tp].x, d[stk[tp].x] = 0;
    		if( stk[tp].b ) rnk[stk[tp].y]--;
    		tp--;
    	}
    }
    bool ans[MAXT]; int n, m, T;
    void solve(int L, int R, vector<edge>e) {
    	int nw = tp, mid = (L + R) >> 1;
    	vector<edge>el, er;
    	for(int i=0;i<e.size();i++) {
    		if( e[i].s <= L && R <= e[i].e ) {
    			if( !unite(e[i].u, e[i].v) ) {
    				for(int j=L;j<=R;j++)
    					ans[j] = false;
    				restore(nw);
    				return ;
    			}
    		}
    		else {
    			if( e[i].s <= mid && e[i].e >= L ) el.push_back(e[i]);
    			if( e[i].e > mid && e[i].s <= R ) er.push_back(e[i]);
    		}
    	}
    	if( L == R ) ans[L] = true;
    	else solve(L, mid, el), solve(mid + 1, R, er);
    	restore(nw);
    }
    int main() {
    	scanf("%d%d%d", &n, &m, &T);
    	for(int i=1;i<=n;i++)
    		fa[i] = i, d[i] = 0, rnk[i] = 1;
    	vector<edge>edges;
    	for(int i=1;i<=m;i++) {
    		int u, v, s, e;
    		scanf("%d%d%d%d", &u, &v, &s, &e), s++;
    		edges.push_back(edge(u, v, s, e));
    	}
    	solve(1, T, edges);
    	for(int i=1;i<=T;i++)
    		printf("%s
    ", ans[i] ? "Yes" : "No");
    }
    

    @details@

    康复计划 - 6。

    事实上只是为了接下来一道数据结构毒瘤题练练手,熟悉一下这个套路。
    结果就做了一个上午。。。
    我果然还是准备准备 AFO 好了。。。

  • 相关阅读:
    java+selenium——查找定位元素,elements复数定位(driver.findElementsByClassName("mnav");)002
    postman——集合——执行集合——测试脚本——pm对象简单示例02
    java+selenium——查找定位元素(driver.findElement(By.id("kw")).click();)001
    java+selenium——查找定位元素,以及对浏览器窗口的简单操作
    java+selenium——Navigate命令
    java+selenium——查找定位元素,elements复数定位(driver.findElementsByClassName("mnav");)001
    java+selenium——浏览器的打开、前进、后退、刷新、退出
    java+selenium——环境搭建
    Android_PreferenceActivity
    The 9th tip of SQL design by DB Query Analyzer
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11081967.html
Copyright © 2011-2022 走看看