ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)
Description
Byteland国家的网络单向传输系统可以被看成是以首都 Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的。现在他们开始在其他城市陆 续建立了新的基站,命令“C x“代表在城市x建立了一个新的基站,不会在同一个城市建立多个基站;城市编号为1到n,其中城市1就是首都Bytetown。在建立基站的过程中他们还 会询问某个城市的网络信号是从哪个城市传输过来的,命令”Q x“代表查询城市x的来源城市。
Input
输 入有多组测试数据。每组数据的第一行包含两个正整数n和m(1 <= n,m <= 100,000),分别代表城市数和命令数。接下来n-1行,每行两个正整数u和v,代表一条从城市u到城市v的网络传输通道。之后的m行,每行一个命 令”C x“或”Q x”。
所有输入的n和m的总和分别都不超过500,000,两组输入数据之间用一个空行隔开。
Output
对于每个查询命令,输出一个整数y,表示来源城市。每两组数据之间用一个空格隔开。
Sample Input
Sample Output
题目大意是给了一个树,一开始所有结点的来源都是编号为1的那个结点。然后可以通过C操作来将某个城市设为来源城市,通过Q操作来查询最近的祖先来源城市。
当时省赛时的第一反应是用带时间戳的线段树去解决,但是没有看清查询的是最近的祖先来源城市,当成了纯粹的染色,果断写跪了。
于是这道题理论上可以有两种解法。不过本地测时,用递归去得到时间戳,深度很深会爆。
解法一:(带时间戳的线段树)
这个和线段树的苹果那题很像,首先通过dfs(当然理论上可以不通过递归实现),得到每个结点的左值和右值;
其中右值代表新编号,即是在dfs中后序遍历的标号。
左值代表子树中右值的最小值,即子树中的最小编号。
通过手画一张图基本上可以知道递归时的操作。
然后就是如何诠释C和Q操作了。
C操作原本是将原编号设为来源城市,即将子树中左值到右值区间内的所有结点染为当前城市编号,由于是染最近祖先来源城市,而且在树中时间戳从上往下是变小的。所以这一步应该是染结点的右值,而且进行懒人操作的pushDown时应该更新右值小的那一个。
Q操作是查询某个点,自然是查询这个点的右值到这个点的右值这个单点区间。得到的是时间戳的右值,再通过Hash回去,得到原编号即可。
解法二:(并查集离线查询)
由于查询操作和点修改操作是混合的,所以查询的时候不能带路径压缩,否则树的结构会被破坏。
但是如果所有的C操作都完成后,对剩余的Q操作便可以进行路径压缩。
于是考虑能不能从最后一个C操作还原到倒数第二个C操作,这样的话,就可以对这两个C直接的Q进行路径压缩。
由于C操作仅是将结点指向自己,所以还原C操作就是将结点指向原来的父节点。这样就只需要记录每个点的父节点。
综上可以对查询进行离线:先正着来一遍,只执行C操作,然后倒着查询,遇到C操作还原,遇到Q操作进行路径压缩,并将查询结果存入数组。
代码:(带时间戳的线段树)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#define LL long long using namespace std; const int maxN = ; //链式前向星
struct Edge
{
int to, next;
}edge[maxN]; int head[maxN], cnt; void addEdge(int u, int v)
{
edge[cnt].to = v;
edge[cnt].next = head[u];
head[u] = cnt;
cnt++;
} void initEdge()
{
memset(head, -, sizeof(head));
cnt = ;
} int n, m;
int idL[maxN], idR[maxN];
int Hash[maxN]; void dfs(int now, int &num)
{
idL[now] = num;
for (int i = head[now]; i != -; i = edge[i].next)
dfs(edge[i].to, num);
idR[now] = num;
Hash[num] = now;
num++;
} //线段树
//区间染色变形,染最值
struct node
{
int lt, rt;
int val;
int turn;
}tree[*maxN]; //向下更新
void pushDown(int id)
{
if (tree[id].turn != )
{
tree[id<<].turn = min(tree[id].turn, tree[id<<].val);
tree[id<<].val = tree[id<<].turn;
tree[id<<|].turn = min(tree[id].turn, tree[id<<|].val);
tree[id<<|].val = tree[id<<|].turn;
tree[id].turn = ;
}
} //建立线段树
void build(int lt, int rt, int id)
{
tree[id].lt = lt;
tree[id].rt = rt;
tree[id].val = idR[];//每段的初值,根据题目要求
tree[id].turn = ;
if (lt == rt)
return;
int mid = (lt + rt) >> ;
build(lt, mid, id<<);
build(mid + , rt, id<<|);
//pushUp(id);
} //修改区间值
void change(int lt, int rt, int id, int v)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
{
tree[id].val = tree[id].turn = min(tree[id].val, v);
return;
}
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
if (lt <= mid)
change(lt, rt, id<<, v);
if (rt > mid)
change(lt, rt, id<<|, v);
//pushUp(id);
} //查询单点的值
int query(int lt, int rt, int id)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
return tree[id].val;
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
if (rt <= mid)
return query(lt, rt, id<<);
if (lt > mid)
return query(lt, rt, id<<|);
} void input()
{
initEdge();
int u, v;
for (int i = ; i < n; ++i)
{
scanf("%d%d", &u, &v);
addEdge(u, v);
}
int num = ;
dfs(, num);
build(, n, );
} void work()
{
char str[];
int v;
for (int i = ; i < m; ++i)
{
scanf("%s%d", str, &v);
if (str[] == 'C')
change(idL[v], idR[v], , idR[v]);
else
printf("%d\n", Hash[query(idR[v], idR[v], )]);
}
} int main()
{
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
while (scanf("%d%d", &n, &m) != EOF)
{
input();
work();
}
return ;
}
代码:(并查集离线查询)
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#define LL long long using namespace std; const int maxN = ; int n, m;
int ufs[maxN];
int fa[maxN];
char op[maxN][];
int query[maxN];
int ans[maxN], top; int findRoot(int x)
{
int pre, now, rx;
rx = x;
while(ufs[rx] != )
rx = ufs[rx];
pre = x;
while(pre != rx)
{
now = ufs[pre];
ufs[pre] = rx;
pre = now;
}
return rx;
} void input()
{
memset(ufs, , sizeof(ufs));
top = ;
int u, v;
for (int i = ; i < n; ++i)
{
scanf("%d%d", &u, &v);
ufs[v] = u;
fa[v] = u;
}
for (int i = ; i < m; ++i)
{
scanf("%s%d", op[i], &query[i]);
if (op[i][] == 'C')
{
ufs[query[i]] = ;
}
}
} void work()
{
for (int i = m-; i >= ; --i)
{
if (op[i][] == 'C')
{
ufs[query[i]] = fa[query[i]];
}
else
{
ans[top++] = findRoot(query[i]);
}
}
while (top)
{
top--;
printf("%d\n", ans[top]);
}
} int main()
{
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
while (scanf("%d%d", &n, &m) != EOF)
{
input();
work();
}
return ;
}
ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)的更多相关文章
- ACM学习历程—SNNUOJ 1116 A Simple Problem(递推 && 逆元 && 组合数学 && 快速幂)(2015陕西省大学生程序设计竞赛K题)
Description Assuming a finite – radius “ball” which is on an N dimension is cut with a “knife” of N- ...
- snnu(1110) 传输网络 (并查集+路径压缩+离线操作 || 线段树)
1110: 传输网络 Time Limit: 3 Sec Memory Limit: 512 MBSubmit: 43 Solved: 18[Submit][Status][Web Board] ...
- hihoCoder 1586 Minimum 【线段树】 (ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛)
#1586 : Minimum 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 You are given a list of integers a0, a1, …, a2 ...
- angry_birds_again_and_again(2014年山东省第五届ACM大学生程序设计竞赛A题)
http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2877 题目描述 The problems ca ...
- ZZUOJ-1195-OS Job Scheduling(郑州大学第七届ACM大学生程序设计竞赛E题)
1195: OS Job Scheduling Time Limit: 2 Sec Memory Limit: 128 MB Submit: 106 Solved: 35 [id=1195&quo ...
- ACM学习历程—SNNUOJ 1239 Counting Star Time(树状数组 && 动态规划 && 数论)
http://219.244.176.199/JudgeOnline/problem.php?id=1239 这是这次陕西省赛的G题,题目大意是一个n*n的点阵,点坐标从(1, 1)到(n, n),每 ...
- ACM学习历程—UESTC 1217 The Battle of Chibi(递推 && 树状数组)(2015CCPC C)
题目链接:http://acm.uestc.edu.cn/#/problem/show/1217 题目大意就是求一个序列里面长度为m的递增子序列的个数. 首先可以列出一个递推式p(len, i) = ...
- ACM学习历程—CSU 1216 异或最大值(xor && 贪心 && 字典树)
题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1216 题目大意是给了n个数,然后取出两个数,使得xor值最大. 首先暴力枚举是C(n, ...
- ACM学习历程—Hihocoder 1291 Building in Sandbox(dfs && 离线 && 并查集)
http://hihocoder.com/problemset/problem/1291 前几天比较忙,这次来补一下微软笔试的最后一题,这题是这次微软笔试的第四题,过的人比较少,我当时在调试B题,没时 ...
随机推荐
- Java 调用OPENOFFIC 转换文档类型
public static void office2PDF(String sourceFile, String destFile) { try { File inputFile = new File( ...
- Anaconda装OpenCV
感谢来源: http://blog.csdn.net/fairylrt/article/details/43560525 前两天看到段子说开源软件就是各种配置,这是一件很辛苦的事情. Anacond ...
- 初识ASP.net-牛腩新闻公布系统
在做牛腩新闻公布的系统的时候,总有一种感觉就是:我仍然在敲机房收费系统,唯一不同的一点.就是敲机房收费的时候,用户界面是是自己手动画界面.而,在牛腩新闻公布系统中,用户界面,却是须要自己 ...
- 【峰回路转】Excel技巧百例 08.计算两个日期的差值
在Excel中假设高速计算两个日期之间的差? 比如A日期为:2012/3/12 B日期为:2015/7/29 那么这两个日期之间差几年,差几个月.差多少天? 我们使用DateDif 函数来处理. ...
- Android开发:LocationManager获取经纬度及定位过程(附demo)
在Android开发其中.常常须要用到定位功能,尤其是依赖于地理位置功能的应用.非常多人喜欢使用百度地图,高德地图提供的sdk.开放API,可是在只须要经纬度,或者城市,街道地址等信息.并不须要提供预 ...
- PostgreSQL 封装操作数据库方法
/// <summary> /// 模块名:操作postgres数据库公共类 /// 作用:根据业务需求对数据库进行操作. /// 注:系统中的公共方法,根据需要,逐一引入 /// 作者: ...
- C#多线程学习(六) 互斥对象
如何控制好多个线程相互之间的联系,不产生冲突和重复,这需要用到互斥对象,即:System.Threading 命名空间中的 Mutex 类. 我们可以把Mutex看作一个出租车,乘客看作线程.乘客首先 ...
- wait() 区别 sleep()
wait() notify() notifyAll() wait和notify方法必须写在synchronized方法内,即在调用wait和notify方法前,需先获得对象锁: 调用wait方法则释放 ...
- SQLServer中游标实例介绍(转)
引言 我们先不讲游标的什么概念,步骤及语法,先来看一个例子: 表一 OriginSalary 表二 AddSalary 现在有2张表,一张是OriginSal ...
- python cookbook第三版学习笔记九:函数
接受任意数量参数的函数. 当传入函数的参数个数很多的时候,在函数定义的时候不需要为每一个参数定义一个变量,可以用*rest的方式来包含多余的参数. 如下面的代码,*rest包含了2,3,4这3个参数. ...