『8.24 模拟赛』ranwen的服务器

题目链接戳这里n(*≧▽≦*)n

题目描述

众所周知,ranwen建造出了强大的服务器网,规模十分庞大,有n个服务器,组成一个树形结构,1号点是总站,但是有时候可能会因为主机被回收,机房断电等事故造成服务器各种GG,现在有m个事件,可能为:1.查询x到根路径上第一个已经挂掉的服务器传输路径 2.x到y路径上的服务器传输路径全挂了。(不存在则输出0)

解题思路

上来先是树剖,然而只过了样例,爆0。。。

题面上提示了正解是并查集,但是并没有想出来,听完题解瞬间懂了。。。。

并查集,当然最方便的是合并操作,但是一个一个删除,显然不方便我们和并,那我们不妨倒着思考,将整棵树一开始看做是坏的,后来慢慢变好,就是慢慢加入新的边,那这样我们就很方便维护了,将并查集的fa连向当前所在的联通的的子树的根,这样我么每次查询的时候就是查询每个节点的最高的父节点向上的边的编号就好了。

但是这样会出现一个问题,一条边,可能在一开始就坏了,后来,又坏了一次,这时候我们加入边的时候就不能在第二次坏掉的时候加入这条边,那怎么办呢?

我们维护一个time数组,表示每个点向上的边最早在哪里坏掉,因为一开始坏掉,那么灾后后面不论你怎么坏,不到一开始这条边一直是坏的。这样,我们正着做一遍并查集,每次合并删除的边上的节点,然后处理出我们的time数组,讲节点深的点向上走,一直getfa到最上面。但如果跳的太远。跳过了LCA怎么办呢?不用担心这一点,因为依然我们能跳过
LCA,那么说明这个点到LCA都已经被合并了,也就是有time的值了,而现在的time值不会对这些边有影响。

所以,我们正着一遍并查集,处理坏掉的,然后暴力枚举边,得到最后好着的边合并起来,再反着做一遍并查集,如果time的值等于当前的时间,那么我们就合并坏的边,最后走到一开始全是好的的情况,将答案记录一下,倒着输出就好了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=1000050;
int n,m,cnt=0,tot=0;
int fa[maxn],head[maxn<<1],ne[maxn<<1],to[maxn<<1],num[maxn<<1],dep[maxn],up[maxn],edge[maxn][2],tim[maxn],ans[maxn],fa_num[maxn];
struct nod{
    int fl,x,y;
};
nod q[maxn];
inline void read(int &x){
    x=0; register char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
inline void add(int f,int t,int nu){
    ne[++cnt]=head[f],head[f]=cnt,to[cnt]=t,num[cnt]=nu;
}
inline void ini(){
    for(register int i=0;i<maxn;i++)fa[i]=i;
}
inline int getfa(int x){
    if(fa[x]==x)return x;
    else return fa[x]=getfa(fa[x]);
}
inline void dfs(int now,int f){
    dep[now]=dep[f]+1;
    for(register int i=head[now];i;i=ne[i]){
        if(to[i]!=f){
            up[to[i]]=now;
            fa_num[to[i]]=num[i];
            dfs(to[i],now);
        }
    }
}
inline void operate(int x,int y,int z){
    x=getfa(x),y=getfa(y);
    while(x!=y){
        if(dep[x]<dep[y])swap(x,y);
        if(!tim[x]){
            tim[x]=z;
            fa[x]=fa[up[x]];
        }
        x=getfa(x);
    }
}
inline void add_edge(int x,int y,int z){
    while(x!=y){
        if(dep[x]<dep[y])swap(x,y);
        if(tim[x]==z){
            fa[x]=fa[up[x]];
        }
        x=up[x];
    }
}
int main(){
    ini();
    read(n),read(m);
    for(register int i=1,f,t;i<n;i++){
        read(f),read(t);
        add(f,t,i),add(t,f,i);
        edge[i][0]=f,edge[i][1]=t;
    }
    dfs(1,0);
    for(register int i=1,j;i<=m;i++){
        read(q[i].fl);
        if(q[i].fl==1)read(q[i].x);
        else {
            read(q[i].x),read(q[i].y);
            operate(q[i].x,q[i].y,i);
        }
    }
    ini();
    for(register int i=1;i<n;i++){
        int u=edge[i][0],v=edge[i][1];
        if(dep[u]<dep[v])swap(u,v);
        if(!tim[u]){
            fa[u]=v;
        }
    }
    for(register int i=m;i>=1;i--){
        if(q[i].fl==1){
            ans[++tot]=fa_num[getfa(q[i].x)];
        }
        else {
            add_edge(q[i].x,q[i].y,i);
        }
    }
    for(register int i=tot;i>=1;i--){
        printf("%d\n",ans[i]);
    }
}

原文地址:https://www.cnblogs.com/Fang-Hao/p/9532070.html

时间: 2024-08-29 16:20:20

『8.24 模拟赛』ranwen的服务器的相关文章

『8.20 模拟赛』旋转的多边形

题目链接戳着里!! 题目描述 解题思路 显然,多边形滚动的时候,指定的点一定是绕着某一个顶点旋转的,旋转的半径就是点到顶点的距离,角度就是顶点所在脚的外角. 如下图所示: 那么我们的问题就转化成了求dis和θ了. dis很简单,只要勾股定理就好了. 那θ呢?也很简单喽,只要链接当前顶点的相邻的两个顶点,运用余弦定理求就好啦,注意一下角度值和弧度制就可以了. (不会余弦定理的小伙伴戳这里==>  Wikipedia & 百度百科) 代码 1 #include<iostream> 2

『8.20 模拟赛』冒泡排序

题目描述 给定n,k,和一个长度为n的序列,请输出这个序列冒泡排序k次之后的结果. 解题思路 我们观察上面给出的伪代码,可以发现这是一段把代码排序成升序的代码,那我们来考虑一下冒牌排序的几个特征. 一个大的数要向右交换,但是一次交换之后就可以换很多位置,所以换一次就不知道跑到哪里去了,所以很难维护. 一个小的数每次最多和它左边的更大的数交换一次,所以一次最多向左边跑一个位置,比大数不知道好维护到那里去了. 进一步思考,k次交换之后,最多可以向左跑k步,也就是说k+1位置的数最远能跑到1的位置上来

『8.21 模拟赛』Victory

题目描述 迟到大王rsw喜欢V这个字母,因为V代表着victory,当一个数字,从左向右数的时候,没有出现过先递减,再递增的情况,就被称作Victory数. 也就是说,这个数字可以是递增的,也可以是递减的,也可以是先递减再递增的,这个过程中可以出现相邻数字相等的情况,但是,就是不能出现过先递减再递增的情况. 问题是:给定n,问:1~n之间有多少个victory数. 解题思路 我们应该一眼就能看出这是一道数位dp题,一开始用3维dp写,状态不够写挂了...后来改用4维就A了. 我们用dp[ i ]

『8.21 模拟赛』技能大赛

题目描述 rsw因为迟到次数太多被列入黑名单,于是被派去参加陕西妇女儿童技能大赛,大赛中共安排了m个比赛项目,算上rsw在内,共有n位选手报名参加本次比赛.(如rsw,zrx,kh,ljm,cky,大耳朵图图,大头儿子等) 经过m场比赛,组委会发现,每个项目,有且仅有两个人实力超群.(比如穿针引线项目,rsw,ljm独领风骚,健美操项目,cky,cjy风姿绰约). 现在,组委会要推选一些人去参加全国比赛,因为每个项目都必须有人擅长,所以推选的这些人,对于每一个项目,至少要有一个人擅长. 已知,选

『8.21 模拟赛』冒泡排序 II

题目描述 前一天的冒泡排序对rsw来说太简单了,所以又有了冒泡排序2,给定n,k,q,问:有多少个不同的1~n的排列,能够使得,冒泡排序k趟后,得到一个几乎正确的序列. 一个几乎正确的序列指的是:它的最长上升子序列的长度至少是n-1. 解题思路 思路就是没有思路.... 昨天刚刚做过一道冒泡排序的题,有一个结论在这里 每个位置上的数最远是由前面的第k个位置转移过来的,并且题目中要求最长上升子序列最短是n-1的长度,所以只有两种情况: 1) 最终是从小到大排序好的 2)最终是从小到大排序好的序列中

『8.25 模拟赛』外卖 (atcoder 100e)

题目链接 题目描述 众所周知,\(cky\)喜欢点外卖. 已知可选的商品有\(n\)种,\(cky\)由于胃容量问题只能点两份(不能一种点两份).\(cky\)要在防止营养过剩的情况下选择美味度最高的搭配. 具体的,对于每第\(i\)个商品,\(i\)正好是其营养成分,\(s_i\)表示其美味度(商品从\(0\)开始编号). 对于每种搭配\((a,b)\),其营养程度为(\(a|b\)其中\(|\)表示二进制下的按位或),其美味度为\(s_a+s_b\). 即\(cky\)要选择满足\(a|b\

2018/5/24模拟赛总结

shzr带病AK虐爆全场... T1n皇后: 这题没啥好说的... T2有重复元素的排列问题: [问题描述] 设R={ r1, r2 , -, rn}是要进行排列的n个元素.其中元素r1, r2 , -, rn可能相同.试设计一个算法,列出R的所有不同排列. [编程任务] 给定n 以及待排列的n 个元素.计算出这n 个元素的所有不同排列. [输入格式] 由perm.in输入数据.文件的第1 行是元素个数n,1≤n≤500.接下来的1 行是待排列的n个元素. [输出格式] 计算出的n个元素的所有不

2019.10.24模拟赛赛后总结

本文原创,如果有不到位的地方欢迎通过右下角的按钮私信我! A.Icow Player 题目描述 被无止境的农活压榨得筋疲力尽后,Farmer John打算用他在MP3播放器市场新买的iCow来听些音乐,放松一下.FJ的iCow里存了N(1 <= N <= 1,000)首曲子,按1..N依次编号.至于曲子播放的顺序,则是按一个Farmer John自己设计的算法来决定: * 第i首曲子有一个初始权值R_i(1 <= R_i <= 10,000). * 当一首曲子播放完毕,接下来播放的

11.24 模拟赛

T1 bzoj 4730 Alice和Bob又在玩游戏 题目大意: Alice和Bob在玩游戏 n个节点,m条边(0<=m<=n-1),构成若干棵有根树,每棵树的根节点是该连通块内编号最小的点 Alice和Bob轮流操作,每回合选择一个没有被删除的节点x,将x及其所有祖先全部删除,不能操作的人输 思路: 根据博弈论的一些定理可以得到一个优秀的$n^2$做法 由$SG$定理得 每个点的$SG$函数值为$mex${子树内一个点的SG xor 该根节点其余儿子的SG} 因此对于每个点我们可以开一个t