【CodeChef】Chef and Graph Queries

Portal --> CC Chef and Graph Queries

Solution

  快乐数据结构题(然而好像有十分优秀的莫队+可撤销并查集搞法qwq)

  首先考虑一种方式来方便一点地。。计算一个图的联通块数量:我们可以考虑容斥,维护每个连通块的生成树,然后\(n-\)生成树边数就是答案了

  这样有一个好,加边的时候比较好处理,但是光这样并不能解决我们的问题

?  顺着这个思路思考,先不考虑时间复杂度,对于一个询问,考虑将编号为\(l\sim r\)的边一条一条加入第\(1\sim l-1\)条边得到的生成树(们)中(先不考虑这个生成树边的选择方式),考虑一条边有贡献(成为新的生成树(们)中的一部分)的情况:

(1)这条边可以替换掉\(1\sim l-1\)中的某条边

(2)这条边的两个端点当前不连通

?  所以问题就变成了,看\(l\sim r\)中有多少条边可以替换掉在生成树中的编号在\(1\sim l-1\)范围内的边再加上(2)情况中的边

  这个时候,我们就可以确定生成树边的选择方式了:因为要让能替换掉在\(1\sim l-1\)范围内的边尽量多,所以一旦当前边可以替换掉另一条边,我们肯定优先选择编号小的替换

  再注意到在考虑询问\((l,r)\)的时候,我们其实相当于要得到\(1\sim r\)的生成树(们),于是我们就可以预处理,按顺序加边,用LCT维护当前的生成树(们),再用一棵主席树(按权值存)维护一下第\(1\sim i\)条边的生成树中,每个编号的边能被多少条编号更大的边替换掉,为了方便查询,那些不需要替换直接加入的边统一加到\(0\)的位置,然后查询的时候只要在第\(r\)棵和第\(l-1\)棵中查一下\([0,l-1]\)的和然后相减一下,再拿\(n\)减一下就是答案了

  最后还有一点就是。。因为要支持删边操作,所以LCT里面把边也看成一个点就好啦ovo

  

  代码大概长这个样子

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2*(1e5)+10,SEG=N*20,inf=2147483647;
int rec[N][2];
int n,m,Q,T;
namespace Lct{/*{{{*/
    const int N=::N*2;
    int ch[N][2],mn[N],fa[N],rev[N],val[N];
    void reset(int x){
        ch[x][0]=ch[x][1]=0; fa[x]=0; val[x]=mn[x]=inf;
    }
    void clear(int n){
        for (int i=1;i<=n;++i) reset(i);
    }
    bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
    int which(int x){return ch[fa[x]][1]==x;}
    void reverse(int x){
        swap(ch[x][0],ch[x][1]);
        rev[x]^=1;
    }
    void pushdown1(int x){
        if (!rev[x]) return;
        if (ch[x][0]) reverse(ch[x][0]);
        if (ch[x][1]) reverse(ch[x][1]);
        rev[x]=0;
    }
    void pushdown(int x){
        if (!isroot(x)) pushdown(fa[x]);
        pushdown1(x);
    }
    void pushup(int x){
        mn[x]=val[x];
        if (ch[x][0]) mn[x]=min(mn[x],mn[ch[x][0]]);
        if (ch[x][1]) mn[x]=min(mn[x],mn[ch[x][1]]);
    }
    void rotate(int x){
        int dir=which(x),f=fa[x];
        if (!isroot(f)) ch[fa[f]][which(f)]=x;
        fa[x]=fa[f]; fa[f]=x;
        if (ch[x][dir^1]) fa[ch[x][dir^1]]=f;
        ch[f][dir]=ch[x][dir^1];
        ch[x][dir^1]=f;
        pushup(f); pushup(x);
    }
    void splay(int x){
        pushdown(x);
        for (int f=fa[x];!isroot(x);f=fa[x]){
            if (!isroot(f))
                rotate(which(f)==which(x)?f:x);
            rotate(x);
        }
        pushup(x);
    }
    void access(int x){
        for (int last=0;x;last=x,x=fa[x]){
            splay(x);
            ch[x][1]=last;
            pushup(x);
        }
    }
    void make_rt(int x){
        access(x);
        splay(x);
        reverse(x);
    }
    bool connected(int x,int y){
        if (x==y) return true;
        make_rt(x);
        access(y);
        splay(y);
        return fa[x];
    }
    void link(int x,int y){
        make_rt(x);
        fa[x]=y;
        access(x);
        splay(x);
    }
    void cut(int x,int y){
        make_rt(x);
        access(y);
        splay(y);
        fa[x]=0;
        ch[y][0]=0;
        pushup(y);
    }
    int query(int x,int y){
        make_rt(x);
        access(y);
        splay(y);
        return mn[y];
    }
}/*}}}*/
namespace Seg{/*{{{*/
    int ch[SEG][2],sum[SEG],rt[SEG];
    int n,tot;
    void clear(){
        for (int i=0;i<=tot;++i)
            ch[i][0]=ch[i][1]=0,sum[i]=0;
        tot=0;
    }
    void init(int _n){clear();n=_n;}
    int newnode(int pre){
        ch[++tot][0]=ch[pre][0]; ch[tot][1]=ch[pre][1]; sum[tot]=sum[pre];
        return tot;
    }
    void _insert(int pre,int &x,int d,int lx,int rx){
        x=newnode(pre);
        ++sum[x];
        if (lx==rx) return;
        int mid=lx+rx>>1;
        if (d<=mid) _insert(ch[pre][0],ch[x][0],d,lx,mid);
        else _insert(ch[pre][1],ch[x][1],d,mid+1,rx);
    }
    void insert(int pre,int x,int d){_insert(rt[pre],rt[x],d,1,n);}
    int _query(int L,int R,int l,int r,int lx,int rx){
        if (!L&&!R) return 0;
        if (l<=lx&&rx<=r) return sum[R]-sum[L];
        int mid=lx+rx>>1,ret=0;
        if (l<=mid) ret+=_query(ch[L][0],ch[R][0],l,r,lx,mid);
        if (r>mid) ret+=_query(ch[L][1],ch[R][1],l,r,mid+1,rx);
        return ret;
    }
    int query(int L,int R,int l,int r){return _query(rt[L-1],rt[R],l,r,1,n);}
}/*}}}*/
void init(){
    Lct::clear(n+m);
    Seg::init(m+1);
}
void debug(int x){
    printf("#%d:\n",x);
    for (int i=0;i<=m;++i) printf("%d ",Seg::query(x-1,x,i+1,i+1));
    printf("\n");
}
void solve(){
    int x,y,tmp;
    for (int i=1;i<=m;++i){
        scanf("%d%d",&rec[i][0],&rec[i][1]);
        x=rec[i][0]; y=rec[i][1];
        if (x==y){
            Seg::rt[i]=Seg::rt[i-1];
            continue;
        }
        Lct::val[n+i]=i;
        if (Lct::connected(x,y)){
            tmp=Lct::query(x,y);
            Lct::cut(rec[i][0],n+tmp);
            Lct::cut(rec[i][1],n+tmp);
            Lct::link(x,n+i);
            Lct::link(y,n+i);
            Seg::insert(i-1,i,tmp+1);
        }
        else{
            Lct::link(x,n+i);
            Lct::link(y,n+i);
            Seg::insert(i-1,i,0+1);
        }
        //debug(i);
    }
    int l,r;
    for (int i=1;i<=Q;++i){
        scanf("%d%d",&l,&r);
        printf("%d\n",n-Seg::query(l,r,0+1,(l-1)+1));
    }
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
#endif
    scanf("%d",&T);
    for (int o=1;o<=T;++o){
        scanf("%d%d%d",&n,&m,&Q);
        init();
        solve();
    }
}

原文地址:https://www.cnblogs.com/yoyoball/p/9745188.html

时间: 2024-11-08 22:52:18

【CodeChef】Chef and Graph Queries的相关文章

[CodeChef - GERALD07 ] Chef and Graph Queries

Read problems statements in Mandarin Chineseand Russian. Problem Statement Chef has a undirected graph G. This graph consists of N vertices and M edges. Each vertex of the graph has an unique index from 1 to N, also each edge of the graph has an uniq

[bzoj3514][CodeChef GERALD07] Chef ans Graph Queries [LCT+主席树]

题面 bzoj上的强制在线版本 思路 首先可以确定,这类联通块相关的询问问题,都可以$LCT$+可持久化记录解决 用LCT维护生成树作为算法基础 具体而言,从前往后按照边的编号顺序扫一遍边 如果这条边两端不在同一个$LCT$联通块中,则$link$ 否则$cut$掉当前连接两条边的路径上的编号最小的边,并$link$ 记录$ntr[i]$表示第$i$条边触发第二种情况时$link$前$cut$掉的边的编号 如果触发第一种情况,则$ntr[i]=0$ 如果为自环,则$ntr[i]=i$ 这样记录之

【xsy2111】 【CODECHEF】Chef and Churus 分块+树状数组

题目大意:给你一个长度为$n$的数列$a_i$,定义$f_i=\sum_{j=l_i}^{r_i} num_j$. 有$m$个操作: 操作1:询问一个区间$l,r$请你求出$\sum_{i=l}^{r} f_i$. 操作2:将$a_x$变成$y$. 此题貌似正常做都不是很好做,考虑用一些奇奇怪怪的做法(比如说分块) 考虑到此题数列在不断地变化,我们考虑用树状数组来维护序列$a$,查询$f_i$的值可以在$O(log n)$的时间内完成. 如果这么做,单次询问的复杂度是$O(n log n)$的,

【CodeChef】Turbo Sort

题目链接:Turbo Sort 用java自带O(NlogN)的排序就可以,java要特别注意输入输出.输入用BufferedReader,输出用printWriter.printWriter的速度比System.out快很多,参考StackOverflow. 代码: 1 import java.io.BufferedOutputStream; 2 import java.io.BufferedReader; 3 import java.io.InputStreamReader; 4 impor

【CodeChef】Factorial(n!末尾0的个数)

The most important part of a GSM network is so called Base Transceiver Station (BTS). These transceivers form the areas called cells (this term gave the name to the cellular phone) and every phone connects to the BTS with the strongest signal (in a l

【CodeChef】Small factorials(BigInteger笔记)

You are asked to calculate factorials of some small positive integers. Input An integer t, 1<=t<=100, denoting the number of testcases, followed by t lines, each containing a single integer n, 1<=n<=100. Output For each integer n given at inpu

【CodeChef】Enormous Input Test

The purpose of this problem is to verify whether the method you are using to read input data is sufficiently fast to handle problems branded with the enormous Input/Output warning. You are expected to be able to process at least 2.5MB of input data p

【CodeChef】Querying on a Grid(分治,最短路)

[CodeChef]Querying on a Grid(分治,最短路) 题面 Vjudge CodeChef 题解 考虑分治处理这个问题,每次取一个\(mid\),对于\(mid\)上的三个点构建最短路径树(因为保证了最短路唯一所以是树). 如果两点之间的最短路径跨越了\(mid\),那么必定有\(dis[u]+dis[v]\)的和就是最短路径长度. 那么我们在分治过程中考虑每一个\(mid\),取其中\(dis[u]+dis[v]\)的最小值,这样子就很容易可以找到最短路径长度. 然后知道了

【CODECHEF】【phollard rho + miller_rabin】The First Cube

All submissions for this problem are available. Read problems statements in Mandarin Chinese and Russian. This problem's statement is really a short one. You are given an integer S. Consider an infinite sequence S, 2S, 3S, ... . Find the first number