CF292D 題解

adam01發表於2024-07-28

\(O(mk\alpha(n))\)

暴力,考慮對於每個詢問 \(l,r\),列舉 \(1\sim l-1,r+1\sim m\),並查集連邊即可。

1154 ms。

\(O(n(m+k\alpha(n)))\)

我們發現列舉 \(i\in [1,l),j\in (r,m]\) 太慢了。

考慮先預處理出並查集從 \(1\) 連邊到編號為 \(id\) 的邊的狀態 \(pre_{id}\),倒過來再處理出 \(suf_{id}\)

詢問 \([l,r]\) 時只需要 \(O(n\alpha(n))\) 合併兩個並查集 \(pre_{l-1},suf_{r+1}\) 即可。

如何合併並查集 \(A,B\):對於每個點 \(i\),在以 \(A\) 為基礎的新並查集裡面合併 \(i\) 分別在 \(A\)\(B\) 中所屬根的編號。

404 ms。

\(O(n^2+k)\)

注意到 \(pre\)\(suf\) 其實只改變了不超過 \(n\) 次(每次合併一個連通塊)。

所以 \(l\) 可以找到小於 \(l\) 的離它最近的 \(pre\) 的變化位置 \(l'\)\(r\) 找到大於 \(r\) 的最近 \(suf\) 變化位置 \(r'\)

答案就是合併 \(pre_{l'},suf_{r'}\) 後連通塊的個數。

注意到 \((l',r')\) 的組合只有 \(n^2\) 種。

於是可以預處理出變化位置,然後再預處理每種 \(l',r'\) 的組合,複雜度 \(O(n^2)\)

總複雜度 \(O(n^2+k)\)

124 ms。

\(O(m\sqrt k\alpha(n))\)

注意到是區間查詢,考慮莫隊。

注意到並查集不太好擴大區間(刪除邊),於是用回滾莫隊即可。

154 ms。


\(O(mk\alpha(n))\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 505, M = 1e4 + 5;

int fa[N], sz[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
inline bool merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return 0;
    if(sz[x] > sz[y]) swap(x, y);
    sz[y] += sz[x];
    fa[x] = y;
    return 1;
}

int u[M], v[M], n, m, k;

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= m; i ++) cin >> u[i] >> v[i];
    cin >> k;
    while(k --)
    {
        int l, r; cin >> l >> r;
        int cnt = n;
        for(int i = 1; i <= n; i ++) fa[i] = i, sz[i] = 1;
        for(int i = 1; i < l; i ++)
            cnt -= merge(u[i], v[i]);
        for(int i = r + 1; i <= m; i ++)
            cnt -= merge(u[i], v[i]);
        cout << cnt << "\n";
    }

    return 0;
}

\(O(n(m+k\alpha(n)))\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 505, M = 1e4 + 5;

struct dsu {
int fa[N], sz[N], cnt;
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
inline void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return;
    if(sz[x] > sz[y]) swap(x, y);
    sz[y] += sz[x];
    fa[x] = y;
    cnt --;
} } pre[M], suf[M];

int u[M], v[M], n, m, k;

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> m;
    
    for(int i = 1; i <= m; i ++) 
        cin >> u[i] >> v[i];
    
    for(int i = 1; i <= n; i ++)
    {
        pre[0].fa[i] = i, pre[0].sz[i] = 1, pre[0].cnt = n;
        suf[m + 1].fa[i] = i, suf[m + 1].sz[i] = 1, suf[m + 1].cnt = n;
    }   
    for(int i = 1; i <= m; i ++)
    {
        pre[i] = pre[i - 1];
        pre[i].merge(u[i], v[i]);
    }
    for(int i = m; i >= 1; i --)
    {
        suf[i] = suf[i + 1];
        suf[i].merge(u[i], v[i]);
    }
    
    cin >> k;
    while(k --)
    {
        int l, r; cin >> l >> r;
        dsu t = pre[l - 1];
        for(int i = 1; i <= n; i ++)
            t.merge(i, suf[r + 1].find(i));
        cout << t.cnt << "\n";
    }

    return 0;
}

\(O(n^2+k)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 505, M = 1e4 + 5;

struct dsu {
int fa[N], cnt;
int find(int x)
{
    while(fa[x] != x)
    {
        x = fa[x] = fa[fa[x]];
    }
    return x;
}
inline bool merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return 0;
    fa[x] = y;
    cnt --;
    return 1;
} } p, s;

int u[M], v[M], n, m, k;
int pos[N], pos2[N], h, c;
int ans[N][N], ppos[M], ppos2[M];
bool vis[N];

signed main()
{
    ios::sync_with_stdio(0);cin.tie(0);
    cin >> n >> m;
    
    for(int i = 1; i <= m; i ++) 
        cin >> u[i] >> v[i];
    
    p.cnt = n;
    s.cnt = n;
    for(int i = 1; i <= n; i ++)
    {
        p.fa[i] = i;
        s.fa[i] = i;
    }
    dsu now;
    ans[0][0] = n;
    for(int i = m; i >= 1; i --)
    {
        if(s.merge(u[i], v[i]))
            pos2[++h] = i, ans[0][h] = s.cnt;
        ppos2[i] = h;
    }
    c = h; h = 0;
    for(int i = 1; i <= c; i ++) vis[i] = 1;
    for(int i = 1; i <= m; i ++)
    {
        if(!p.merge(u[i], v[i]))
        {
            ppos[i] = h;
            continue;
        }
        now = p;
        if(i) pos[++h] = i;
        ppos[i] = h;
        ans[h][0] = now.cnt;
        for(int j = 1; pos2[j] >= i; j ++)
        {
            now.merge(u[pos2[j]], v[pos2[j]]);
            ans[h][j] = now.cnt;
        }
    }
    
    cin >> k;
    while(k --)
    {
        int l, r; cin >> l >> r;
        int p1 = ppos[l - 1], p2 = ppos2[r + 1];
        cout << ans[p1][p2] << "\n";
    }

    return 0;
}

\(O(m\sqrt k\alpha(n))\)

#include<bits/stdc++.h>
#define blk(_X) (blk[_X])
using namespace std;
typedef long long ll;
#define int ll

const int M = 2e4 + 5, N = 505;

int n, m, k, sz;
int blk[M], ans[M];
struct node{int l, r, id;};
node q[M];

bool cmpq(node x, node y) {return blk(x.l) == blk(y.l) ? x.r > y.r : x.l < y.l;}

vector<pair<int, int>> logs;
struct dsu
{
    int fa[N], cnt;
    inline int find(int x)
    {
        while(fa[x] != x)
        {
            x = fa[x] = fa[fa[x]];
        }
        return x;
    }
    inline bool merge(int x, int y)
    {
        x = find(x), y = find(y);
        if(x == y) return 0;
        fa[x] = y;
        cnt --;
        return 1;
    }
    void init(int n)
    {
        for(int i = 1; i <= n; i ++) fa[i] = i;
        cnt = n;
    }
} t;

void init()
{
    sz = m / sqrt(k) + 70;
    for(int i = 1; i <= m + 1; i ++)
        blk[i] = i / sz;
}

int u[M], v[M];
int upd(int c)
{
    int x = t.find(u[c]), y = t.find(v[c]);
    if(x == y) return 0;
    t.fa[x] = y;
    t.cnt --;
    return 1;
}

signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= m; i ++) cin >> u[i] >> v[i];
    cin >> k;
    for(int i = 1; i <= k; i ++) cin >> q[i].l >> q[i].r, q[i].l --, q[i].r ++, q[i].id = i;
    init();

    sort(q + 1, q + k + 1, cmpq);
    
    int l = -1, r;
    for(int i = 1; i <= k; i ++)
    {
        if(l < 0 || blk(l) != blk(q[i].l))
        {
            t.init(n);
            for(int j = m; j >= q[i].r; j --) upd(j);
            for(int j = 1; j <= blk(q[i].l) * sz; j ++) upd(j);
            r = q[i].r, l = blk(q[i].l) * sz;
        }

        while(r > q[i].r) r --, upd(r);

        int Ll = l;
        dsu bak = t;

        while(l < q[i].l) l ++, upd(l);
        
        ans[q[i].id] = t.cnt;

        l = Ll;
        t = bak;
    }
    for(int i = 1; i <= k; i ++)
        cout << ans[i] << "\n";

    return 0;
}