• @codeforces



    @description@

    给定一个 n 个点的无向图,标号从 1 到 n。一开始没有任何边存在。

    请你完成以下两种操作:
    1 x y(1 <= x, y <= n, x ≠ y),将 (x, y) 这一条边置反。也就是,存在变为不存在,不存在变为存在。
    2 x y(1 <= x, y <= n, x ≠ y),询问 x, y 是否连通。

    注意操作是加密的。记 last 表示上一次 2 操作的结果(连通为 1,否则为 0),初始为 0。
    则真实的询问编号 x', y' 应为 (x + last - 1) mod n + 1,(y + last - 1) mod n + 1。

    Input
    第一行输入两个整数 n, m,表示点数与操作数。
    接下来 m 行,每行三个整数,描述了一次操作。

    Output
    输出一个字符串,第 i 个字符表示第 i 次 2 操作的答案。

    Examples
    Input
    5 9
    1 1 2
    1 1 3
    2 3 2
    1 2 4
    2 3 4
    1 2 4
    2 3 4
    1 1 3
    2 4 3
    Output
    1010

    @solution@

    (据说做法很多,不过我觉得官方题解挺好的,所以就只写了官方题解的方法)

    众所周知codeforces的在线修改查询都是通过交互实现的,所以这个题一定是假在线。

    这道题其实是一道很经典的模型:动态图问题。即在动态加边删边中维护整张图的连通性。
    而众所周知的动态图解法如 lct,线段树分治 + 并查集等都是要支持离线的。

    但其实,这道题所谓的在线只有两种情况 last = 0 与 last = 1。
    至少我们还是不能用 lct,因为 lct 需要知道一条边被删除的时间。

    考虑使用一些更暴力的方法,比如——对询问分块。
    我们将询问分成 Q 块。感性点的思路是:之前的整块大力重构维护出一个信息,零散的操作暴力算它的贡献。

    更进一步地,假如我们处理第 i 块的询问时。
    先找出第 i 块中可能涉及到的边(即 last = 0/1 分别对应的边),这样的边一共会有 2*m/Q 条。
    然后对于 1~i-1 块这些加删边,如果第 i 块中没有涉及,就统计一条边被操作的次数,使用并查集缩点。这部分的总复杂度是 O(Q*m)。
    如果涉及到了,就建出一个图。因为只拿涉及到的边建图,所以总边数 < 2*m/Q。

    然后对于散块,操作时标记图中的边出现还是不出现,查询暴力 dfs。
    因为图中的边数 < 2*m/Q,所以 dfs 复杂度为 O(m/Q)。dfs 这部分的总复杂度为 O(m^2/Q)。

    那么总复杂度为 O(Q*m + m^2/Q)。当 Q = √m 时,取最小值 O(m√m)。

    @accepted code@

    #include<map>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define rep(G, x) for(Graph::edge *p = G.adj[x];p;p = p->nxt)
    #define pii pair<int, int>
    #define fi first
    #define se second
    const int MAXN = 400000;
    const int SQRT = 450;
    struct Graph{
    	struct edge{
    		int to, id;
    		edge *nxt;
    	}edges[MAXN + 5], *adj[MAXN + 5], *ecnt;
    	void clear(int n) {
    		ecnt = &edges[0];
    		for(int i=1;i<=n;i++)
    			adj[i] = NULL;
    	}
    	void addedge(int u, int v, int id) {
    		edge *p = (++ecnt);
    		p->to = v, p->id = id, p->nxt = adj[u], adj[u] = p;
    		p = (++ecnt);
    		p->to = u, p->id = id, p->nxt = adj[v], adj[v] = p;
    	}
    }G;
    int le[MAXN + 5], ri[MAXN + 5], num[MAXN + 5], bcnt;
    void init(int m) {
    	for(int i=1;i<=m;i++) {
    		if( (i - 1) % SQRT == 0 )
    			bcnt++, le[bcnt] = i;
    		ri[bcnt] = i, num[i] = bcnt;
    	}
    }
    int fa[MAXN + 5];
    int find(int x) {
    	return fa[x] = (fa[x] == x ? x : find(fa[x]));
    }
    void unite(int x, int y) {
    	int fx = find(x), fy = find(y);
    	if( fx != fy )
    		fa[fx] = fy;
    }
    vector<pii>e;
    bool tag[MAXN + 5]; int tg2[MAXN + 5];
    void update(int p) {
    	if( tg2[p] == -1 ) {
    		G.addedge(find(e[p].fi), find(e[p].se), p);
    		tg2[p] = 1;
    	}
    	else tg2[p] ^= 1;
    }
    map<pii, int>mp;
    int id(pii p) {
    	if( p.fi > p.se ) swap(p.fi, p.se);
    	if( mp.count(p) ) return mp[p];
    	else {
    		e.push_back(p);
    		return mp[p] = e.size() - 1;
    	}
    }
    bool vis[MAXN + 5];
    void dfs(int x, bool f) {
    	if( vis[x] == f ) return ;
    	vis[x] = f;
    	rep(G, x) {
    		if( tg2[p->id] )
    			dfs(p->to, f);
    	}
    }
    bool query(int p) {
    	dfs(find(e[p].fi), true);
    	bool ret = vis[find(e[p].se)];
    	dfs(find(e[p].fi), false);
    	return ret;
    }
    bool lans[MAXN + 5];
    int t[MAXN + 5], qry[2][MAXN + 5];
    int n, m;
    int main() {
    	scanf("%d%d", &n, &m), init(m);
    	for(int i=1;i<=m;i++) {
    		int x, y;
    		scanf("%d%d%d", &t[i], &x, &y);
    		qry[0][i] = id(make_pair(x, y));
    		x = x % n + 1, y = y % n + 1;
    		qry[1][i] = id(make_pair(x, y));
    	}
    	int lastans = 0;
    	for(int i=1;i<=bcnt;i++) {
    		for(int j=1;j<le[i];j++) {
    			tag[qry[lans[j]][j]] = false;
    			tg2[qry[lans[j]][j]] = -1;
    		}
    		for(int j=le[i];j<=ri[i];j++) {
    			tag[qry[0][j]] = tag[qry[1][j]] = true;
    			tg2[qry[0][j]] = tg2[qry[1][j]] = -1;
    		}
    		for(int j=1;j<=n;j++) fa[j] = j;
    		G.clear(n);
    		for(int j=1;j<le[i];j++)
    			if( t[j] == 1 && !tag[qry[lans[j]][j]] ) {
    				if( tg2[qry[lans[j]][j]] == -1 ) tg2[qry[lans[j]][j]] = 1;
    				else tg2[qry[lans[j]][j]] ^= 1;
    			}
    		for(int j=1;j<le[i];j++)
    			if( t[j] == 1 && !tag[qry[lans[j]][j]] && tg2[qry[lans[j]][j]] ) {
    				unite(e[qry[lans[j]][j]].fi, e[qry[lans[j]][j]].se);
    			}
    		for(int j=1;j<le[i];j++)
    			if( t[j] == 1 && tag[qry[lans[j]][j]] )
    				update(qry[lans[j]][j]);
    		for(int j=le[i];j<=ri[i];j++) {
    			if( t[j] == 1 )
    				update(qry[lastans][j]), lans[j] = lastans;
    			else lastans = lans[j] = query(qry[lastans][j]);
    		}
    	}
    	for(int i=1;i<=m;i++)
    		if( t[i] == 2 )
    			putchar(char(lans[i]) + '0');
    }
    

    @details@

    对询问分块之类的题,虽然很少见,但其实非常巧妙。

    而且代码量比起直接上大数据结构模板,往往能减少很多。

  • 相关阅读:
    MongoDB Http Interface
    从python2.7和python3.0的语法差异总结
    从python2.7和python3.0的语法差异总结
    MongoDB聚合aggregate
    MongoDB聚合aggregate
    Windows 有没有办法查看文件被哪个进程占用
    Windows 有没有办法查看文件被哪个进程占用
    开启mongodb 的web
    开启mongodb 的web
    MongoDB基本命令操作
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11740365.html
Copyright © 2020-2023  润新知