校内模拟赛 旅行(by NiroBC)

题意:

  n个点的无向图,Q次操作,每次操作可以连接增加一条边,询问两个点之间有多少条边是必经之路。如果不连通,输出-1。

分析:

  首先并查集维护连通性,每次加入一条边后,如果这条边将会连接两个联通块,那么lct连接两个点,边权化为点权,新增一个点,点权为1。否则,构成了环,环上的边都变为0,lct维护覆盖标记。询问就是对一条链进行询问。

  离线+树剖的做法:从前往后建出树,如果出现环则不加入,然后树剖,每次出现一条非树边就是将环上的边赋值为0,询问就是两点之间的边权和。

代码:

lct

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cctype>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch==‘-‘)f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-‘0‘;return x*f;
}

const int N = 200005;
struct LCT{
    #define lc ch[rt][0]
    #define rc ch[rt][1]
    int siz[N], val[N], ch[N][2], fa[N], sk[N], rev[N], tag[N], Index;
    inline bool isroot(int x) { return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
    inline bool son(int x) { return x == ch[fa[x]][1]; }
    inline void pushup(int rt) { siz[rt] = siz[lc] + siz[rc] + val[rt]; }
    inline void pushdown(int rt) {
        if (rev[rt]) {
            swap(lc, rc);
            rev[lc] ^= 1, rev[rc] ^= 1; rev[rt] ^= 1;
        }
        if (tag[rt]) {
            tag[lc] = tag[rc] = 1;
            siz[lc] = siz[rc] = val[lc] = val[rc] = tag[rt] = 0;
        }
    }
    void rotate(int x) {
        int y = fa[x], z = fa[y], c = son(y), b = son(x), a = ch[x][!b];
        if (!isroot(y)) ch[z][c] = x; fa[x] = z;
        ch[x][!b] = y, fa[y] = x;
        ch[y][b] = a; if (a) fa[a] = y;
        pushup(y); pushup(x);
    }
    void splay(int x) {
        int top = 1; sk[top] = x;
        for (int i = x; !isroot(i); i = fa[i]) sk[++top] = fa[i];
        while (top) pushdown(sk[top --]); // 注意下pushdown到x的下一层
        while (!isroot(x)) {
            int y = fa[x], z = fa[y];
            if (isroot(y)) rotate(x);
            else {
                if (son(x) == son(y)) rotate(y), rotate(x);
                else rotate(x), rotate(x);
            }
        }
    }
    void access(int x) {
        for (int last = 0; x; last = x, x = fa[x])
            splay(x), ch[x][1] = last, pushup(x);
    }
    void makeroot(int x) {
        access(x); splay(x); rev[x] ^= 1;
    }
    void link(int x,int y) {
        makeroot(x); fa[x] = y;
    }
    void add(int x,int y) {
        val[++Index] = 1; link(x, Index); link(Index, y);
    }
    void split(int x,int y) {
        makeroot(x); access(y); splay(y);
    }
    #undef lc
    #undef rc
}lct;
int fa[N];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int main() {
    int n = read(), m = read(); lct.Index = n;
    for (int i = 1; i <= n; ++i) fa[i] = i;
    while (m --) {
        int opt = read(), x = read(), y = read(), tx = find(x), ty = find(y);
        if (opt == 1) {
            if (tx != ty) fa[tx] = ty, lct.add(x, y);
            else lct.split(x, y), lct.tag[y] = 1, lct.val[y] = lct.siz[y] = 0;
        } else {
            if (tx != ty) puts("-1");
            else lct.split(x, y), printf("%d\n", lct.siz[y]);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/mjtcn/p/10645044.html

时间: 2024-11-11 11:56:16

校内模拟赛 旅行(by NiroBC)的相关文章

校内模拟赛 虫洞(by NiroBC)

题意: n个点m条边的有向图,每一天每条边存在的概率都是p,在最优策略下,询问从1到n的期望天数. 分析: dijkstra. 每次一定会优先选dp最小的后继走,如果这条边不存在,选次小的,以此类推. dp[i]表示从i开始到n的期望天数,从后往前推,每次取出dp最小的,更新其他点. 代码: #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include&l

2017.6.11 校内模拟赛

题面及数据及std(有本人的也有原来的) :2017.6.11 校内模拟赛 T1 自己在纸上模拟一下后就会发现 可以用栈来搞一搞事情 受了上次zsq 讲的双栈排序的启发.. 具体就是将原盘子大小copy一下排个序 用两个指针维护两个数组(原数据 和 排序后的数据), 即分为1数据和2数组 将小于1指针指向的数据的2数组中的数据全部压入栈中 后进行消除, 将栈栈顶元素与当前1数组中的1指针指向的元素进行比较 相同则消除 后重复过程 直至指针超过N 后判断一下是否两个指针都超过了N... #incl

校内模拟赛 Zbq&#39;s Music Challenge

Zbq's Music Challenge 题意: 一个长度为n的序列,每个位置可能是1或者0,1的概率是$p_i$.对于一个序列$S$,它的得分是 $$BasicScore=A\times \sum_{i=1}^{n}{S_i} \tag{1}$$ $$ combo(i)=\left\{ \begin{aligned} &S_i & &i=1 \\ &combo(i-1)+1 & &i\neq 1 ~\mathrm{and}~ S_i=1 \\ &

[20180815]校内模拟赛

T1 游戏(game) 问题描述 Alice准备和Bob玩一个游戏,他们先拿出若干堆石子,每一堆里面都有一定数量的石子. Alice和Bob轮流操作,Alice先手,每次操作需要选择一堆石子数量大等于2的石子,把这堆石子分成两堆. 假设这堆石子中有x个石子,那么可以分成一堆y(1≤y<x)个石子和一堆x-y个石子. 如果轮到一个人操作时没有可选的石子堆,这个人就输了. Alice有n堆石子,其中第i堆有\(a_i\)个石子,他打算选出其中连续一段石子跟Bob玩. 你需要回答m次询问,每次查询取出

[20180816]校内模拟赛

T1 清理(clear) 问题描述 小 C 最近自己开发了一款云盘软件,目前已有??个用户.小C 的云盘上的文件会被后台分成两种类型,活动 文件和非活动文件,活动文件即可能常用的文件,会被放在高速服务器上给用户提供高速下载服务.用户 上传一个文件时,这个文件会被设置为活动文件.由于高速服务器内存大小有限,小 C 需要把一些文件 设为非活动文件,有以下两种设置方式:1.把上传时间前??早的文件全部设为非活动文件:2.把第??个用户上 传的文件全部设为非活动文件.注意这两种方式操作的对象都是所有文件

校内模拟赛T1大美江湖

这就是一个模拟题,注意1234分别对应左右上下横坐标和纵坐标的判断就好了 题解: 需要注意的是,向上取整ceil函数是对于一个double值返回一个double值,也就是说在ceil里面的类型一定要是double,否则会炸 代码: #include<cstdio> #include<iostream> #include<cstdlib> #include<cmath> #include<cstring> #include<string>

校内模拟赛:确定小组

  [问题描述] 有n个人坐成一排,这n个人都在某一个小组中,同一个小组的所有人所坐的位置一定是连续的. 有一个记者在现场进行采访,他每次采访都会询问一个人其所在的小组有多少人,被询问的每个人都给出了正确的答案,但是由于时间仓促,记者不一定询问了每个人,我们记录一个长度为n的答案序列,序列的第i个数表示第i个人的回答,如果为0则表示记者没有询问过这个人. 记者发现,对于一些情况,他可以唯一确定这排所有人的分组,而对于另外一些情况则不能,于是记者开始好奇,对于某一个答案序列,他能不能做到这一点,如

校内模拟赛20170604

香蕉锤--双向链表 #include<iostream> #include<cstdio> using namespace std; inline int read(){ int num=0,t=1;char c=getchar(); while(c>'9'||c<'0'){if(c=='-')t=-1;c=getchar();} while(c>='0'&&c<='9'){num=num*10+c-'0';c=getchar();} ret

5.13 校内模拟赛

... 果然是dalao们做难题做多了后 简单题水不起来了吗.. 5.13解题报告 300分 T1写了差不多30~40分钟 T2写了不到5min (当时怀疑有坑..) T3推了大概1个多小时的式子, 然后又加上调试差不多一个半小时 时间分配就这样..感觉T2出的太过简单了..直接是个模板 T1 并查集 + 乱搞 T2 快速幂模板 T3 Dp T1 : codevs 2796 最小完全图 二次联通门 : codevs 2796 最小完全图 /* codevs 2796 最小完全图 并查集 + 乱搞