zoukankan      html  css  js  c++  java
  • loj #121. 「离线可过」动态图连通性

    嘟嘟嘟


    我用的方法是线段树+并差集,简单易懂,常数略大。


    我们按操作时间建立线段树,并且每一个节点开一个vector,记录这个区间内有哪些边。然后算出每一条边的出现时间(这个时间必须是一段连续的区间,如果他加入后删除又加入,那就算两条边),像区间更新打标记一样把这条边放进对应节点的vector。


    对于询问,我们只用在这个时间点对应的节点上标记一下即可。显然每一个叶子结点最多只能有一个询问。


    接下来,我们dfs整棵线段树,走到一个节点,就把这个节点的vector里面的所有边加进并差集中。到了叶子结点的时候,如果有询问,就并差集查一下连通性即可。然后回溯的时候,我们要撤销所有的合并操作,即维护一个可撤销的并差集(撤销最后一条加进来的边)。这个也不难,我们每一次按秩合并的时候用启发式合并,并且不要路径压缩,然后撤销的时候恢复两个集合原来的代表元和大小即可。
    写起来很简单:

    int p[maxn], siz[maxn], top = 0;
    pr st[maxm << 2];
    In int Find(int x) {return x == p[x] ? x : Find(p[x]);}
    In int merge(int x, int y)
    {
    	int px = Find(x), py = Find(y);
    	if(px == py) return 0;
    	if(siz[px] > siz[py]) swap(px, py);
    	p[px] = py, siz[py] += siz[px];
    	st[++top] = mp(px, py);
    	return 1;
    }
    In void cancel()
    {
    	int x = st[top].first, y = st[top].second; --top;
    	p[x] = x, siz[y] -= siz[x];
    }
    

    分析一下复杂度,对于每一个区间,在线段树上最多只会被分成$log$个,因此区间总数量$mlogm$个,然后并差集复杂度$logn$,而遍历线段树的复杂度和并差集操作是独立的,所以不影响,因此总复杂度$O(mlogmlogn)$。不过并差集的$logn$很小,实际上跑的还是蛮快的。 ```c++ #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define In inline typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 5e3 + 5; const int maxm = 5e5 + 5; In ll read() { ll ans = 0; char ch = getchar(), las = ' '; while(!isdigit(ch)) las = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(las == '-') ans = -ans; return ans; } In void write(ll x) { if(x < 0) putchar('-'), x = -x; if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } In void MYFILE() { #ifndef mrclr freopen("ha.in", "r", stdin); freopen("ha.out", "w", stdout); #endif }

    define pr pair<int, int>

    define mp make_pair

    int n, m, tim[maxn][maxn];
    pr q[maxm];

    int l[maxm << 2], r[maxm << 2], pos[maxm];
    bool vis[maxm << 2];
    vector v[maxm << 2];
    In void build(int L, int R, int now)
    {
    l[now] = L, r[now] = R;
    if(L == R) {pos[L] = now; return;}
    int mid = (L + R) >> 1;
    build(L, mid, now << 1), build(mid + 1, R, now << 1 | 1);
    }
    In void update_E(int L, int R, int now, pr E)
    {
    if(l[now] == L && r[now] == R) {v[now].push_back(E); return;}
    int mid = (l[now] + r[now]) >> 1;
    if(R <= mid) update_E(L, R, now << 1, E);
    else if(L > mid) update_E(L, R, now << 1 | 1, E);
    else update_E(L, mid, now << 1, E), update_E(mid + 1, R, now << 1 | 1, E);
    }

    int p[maxn], siz[maxn], top = 0;
    pr st[maxm << 2];
    In int Find(int x) {return x == p[x] ? x : Find(p[x]);}
    In int merge(int x, int y)
    {
    int px = Find(x), py = Find(y);
    if(px == py) return 0;
    if(siz[px] > siz[py]) swap(px, py);
    p[px] = py, siz[py] += siz[px];
    st[++top] = mp(px, py);
    return 1;
    }
    In void cancel()
    {
    int x = st[top].first, y = st[top].second; --top;
    p[x] = x, siz[y] -= siz[x];
    }

    int ans[maxm];
    In void dfs(int now)
    {
    int cnt = 0;
    for(auto i : v[now]) cnt += merge(i.first, i.second);
    if(l[now] == r[now])
    {
    if(vis[l[now]]) ans[l[now]] = Find(q[l[now]].first) == Find(q[l[now]].second);
    for(int i = 1; i <= cnt; ++i) cancel();
    return;
    }
    dfs(now << 1), dfs(now << 1 | 1);
    for(int i = 1; i <= cnt; ++i) cancel();
    }

    int main()
    {
    // MYFILE();
    n = read(), m = read();
    build(1, m, 1);
    int FLG = 0;
    for(int i = 1; i <= m; ++i)
    {
    int op = read(), x = read(), y = read();
    if(x > y) swap(x, y);
    if(!op) tim[x][y] = i;
    else if(op == 1) update_E(tim[x][y], i, 1, mp(x, y)), tim[x][y] = 0;
    else q[i] = mp(x, y), FLG = vis[i] = 1;
    }
    if(!FLG) return 0;
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n; ++j) if(tim[i][j]) update_E(tim[i][j], m, 1, mp(i, j));
    for(int i = 1; i <= n; ++i) p[i] = i, siz[i] = 1;
    dfs(1);
    for(int i = 1; i <= m; ++i) if(vis[i]) puts(ans[i] ? "Y" : "N");
    return 0;
    }

  • 相关阅读:
    Write an algorithm such that if an element in an MxN matrix is 0, its entire row and column is set to 0.
    旋转二维数组
    replace empty char with new string,unsafe method和native implementation的性能比较
    判断一字符串是否可以另一字符串重新排列而成
    移除重复字符的几个算法简单比较
    也来纠结一下字符串翻转
    判断重复字符存在:更有意义一点
    程序员常去网站汇总
    sublime
    针对程序集 'SqlServerTime' 的 ALTER ASSEMBLY 失败,因为程序集 'SqlServerTime' 未获授权(PERMISSION_SET = EXTERNAL_ACCESS)
  • 原文地址:https://www.cnblogs.com/mrclr/p/11049098.html
Copyright © 2011-2022 走看看