首页 > 其他分享 >CF292D 题解

CF292D 题解

时间:2024-07-28 18:17:11浏览次数:13  
标签:cnt return int 题解 cin fa CF292D find

\(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;
}

标签:cnt,return,int,题解,cin,fa,CF292D,find
From: https://www.cnblogs.com/adam01/p/18328639

相关文章

  • CF626G Raffles 题解
    Description有\(n\)个奖池,第\(i\)个奖池的奖金是\(p_i\),已经有\(l_i\)张彩票押在上面。现在你有\(t\)张彩票,你需要将你的彩票分配到这些奖池中,并且保证你在每个奖池中押的彩票数不能超过该奖池原有的彩票数。若你在第\(i\)个奖池中押了\(t_i\)张彩票,则你中奖的概......
  • AtCoder Beginner Contest 363 题解 A-D(待补充)
    A-PilingUp1.1思路其实就是向上取百位的整,需要增加多少,123则为200-123=177;1.2代码voidsolve(){intn;cin>>n;intt=n/100;cout<<(t+1)*100-n;}B-JapaneseCursedDoll 2.1思路就是判断最少需要多少天,会有大于等于P个人......
  • Codeforces Round 962 (Div. 3) 题解 A-F
    A.LegsProblem-A-Codeforces1.1翻译农夫约翰的农场又迎来了美好的一天。农夫约翰来到农场后,数了数n条腿。众所周知,农场里只住着鸡和牛,一只鸡有2条腿,而一头牛有4条腿。假设约翰农场主数清了所有动物的腿,那么他的农场里最少有多少动物?1.2思路求最少有几只动物......
  • 题解 - 矩阵
    题目描述小明和小花知道学信息学竞赛的学生特别擅长做一些和矩阵相关的问题。例如,同学经常做的一个题目,给你一个N×M的矩阵,矩阵里面每个格子上都有一个数,要从左上角(1,1),走到右下角(N,M),每一步只能往下或者往右走,让你求经过的路径上数的总和最小。小明和小花发现这个......
  • luogu P1896 [SCOI2005] 互不侵犯 题解
    luoguP1896[SCOI2005]互不侵犯题解题目传送门思路状态压缩dp。状态压缩dp对于每一行,用一个\(n\)位二进制数表示每行的状态,则对于上下两行之间,设上行的数字为\(a\),下行的数字为\(b\),状态不合法有三种情况:\(a\operatorname{and}b\neq0\),即存在上行与下行同......
  • 洛谷P2440 题解
    P2440题解题目传送门提取关键词,题目需要的是数量大于$k$的最大$l$,考虑二分答案。可以二分枚举$l$的值,check函数中检验切割出该长度小段木头的个数,与$k$进行比较。小数据模拟例如,我们有五根木棍且$k=4$,分别为374106第一次循环$l=0,r=9,mid=4$。check函数中得到......
  • luogu P1896 [SCOI2005] 互不侵犯 题解
    luoguP1896[SCOI2005]互不侵犯题解题目传送门思路状态压缩dp。状态压缩dp对于每一行,用一个\(n\)位二进制数表示每行的状态,则对于上下两行之间,设上行的数字为\(a\),下行的数字为\(b\),状态不合法有三种情况:\(a\operatorname{and}b\neq0\),即存在上行与下行同......
  • Codeforces Round 962 (Div. 3) A - D详细题解(思路加代码Python,C++(垃圾灰名小白想
             吐槽一下,这次比赛不知道怎么的,可能是div3参加的人比较多吗,代码题解上去后全是inqueue,比赛的过程中我还看了提交的,80多页几千个提交全是inqueue,我的代码等了**半个多小时才运行,然后发现timelimit真的有点搞心态,思路在下一题我还要反过来去优化上一题,不过......
  • 逆序对的数量 - 题解
    逆序对的数量时间限制:C/C++1000MS,其他语言2000MS内存限制:C/C++64MB,其他语言128MB描述给定一个长度为\(n\)的整数数列,请你计算数列中的逆序对的数量。逆序对的定义如下:对于数列的第\(i\)个和第\(j\)个元素,如果满足\(i<j\)且\(a[i]>a[j]\),则其为一个逆序对;否则......
  • ABC364题解(D-G)
    D先对\(a\)从小到大排序。将题目转化成找到最小的\(d\),使得恰好有\(k\)个\(a_i\in[b-d,b+d]\)。对于每个询问\(b,k\),考虑二分答案。设待检查的答案为\(d\),二分找到最小的\(p1\)使得\(a_{p1}\geqb-d\)和最小的\(p2\)使得\(a_{p2}>b+d\),包含的数的个数即为\(......