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

3 4

1 2

2 3

Q 3

C 2

Q 2

Q 3

Sample Output

1

2

2

题目大意是给了一个树,一开始所有结点的来源都是编号为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 = 100005;

//链式前向星
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, -1, sizeof(head));
    cnt = 0;
}

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 != -1; 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[4*maxN];

//向下更新
void pushDown(int id)
{
    if (tree[id].turn != 0)
    {
        tree[id<<1].turn = min(tree[id].turn, tree[id<<1].val);
        tree[id<<1].val = tree[id<<1].turn;
        tree[id<<1|1].turn = min(tree[id].turn, tree[id<<1|1].val);
        tree[id<<1|1].val = tree[id<<1|1].turn;
        tree[id].turn = 0;
    }
}

//建立线段树
void build(int lt, int rt, int id)
{
    tree[id].lt = lt;
    tree[id].rt = rt;
    tree[id].val = idR[1];//每段的初值,根据题目要求
    tree[id].turn = 0;
    if (lt == rt)
        return;
    int mid = (lt + rt) >> 1;
    build(lt, mid, id<<1);
    build(mid + 1, rt, id<<1|1);
    //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) >> 1;
    if (lt <= mid)
        change(lt, rt, id<<1, v);
    if (rt > mid)
        change(lt, rt, id<<1|1, 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) >> 1;
    if (rt <= mid)
        return query(lt, rt, id<<1);
    if (lt > mid)
        return query(lt, rt, id<<1|1);
}

void input()
{
    initEdge();
    int u, v;
    for (int i = 1; i < n; ++i)
    {
        scanf("%d%d", &u, &v);
        addEdge(u, v);
    }
    int num = 1;
    dfs(1, num);
    build(1, n, 1);
}

void work()
{
    char str[5];
    int v;
    for (int i = 0; i < m; ++i)
    {
        scanf("%s%d", str, &v);
        if (str[0] == ‘C‘)
            change(idL[v], idR[v], 1, idR[v]);
        else
            printf("%d\n", Hash[query(idR[v], idR[v], 1)]);
    }
}

int main()
{
    //freopen("test.in", "r", stdin);
    //freopen("test.out", "w", stdout);
    while (scanf("%d%d", &n, &m) != EOF)
    {
        input();
        work();
    }
    return 0;
}

代码:(并查集离线查询)

#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 = 100005;

int n, m;
int ufs[maxN];
int fa[maxN];
char op[maxN][3];
int query[maxN];
int ans[maxN], top;

int findRoot(int x)
{
    int pre, now, rx;
    rx = x;
    while(ufs[rx] != 0)
        rx = ufs[rx];
    pre = x;
    while(pre != rx)
    {
        now = ufs[pre];
        ufs[pre] = rx;
        pre = now;
    }
    return rx;
}

void input()
{
    memset(ufs, 0, sizeof(ufs));
    top = 0;
    int u, v;
    for (int i = 1; i < n; ++i)
    {
        scanf("%d%d", &u, &v);
        ufs[v] = u;
        fa[v] = u;
    }
    for (int i = 0; i < m; ++i)
    {
        scanf("%s%d", op[i], &query[i]);
        if (op[i][0] == ‘C‘)
        {
            ufs[query[i]] = 0;
        }
    }
}

void work()
{
    for (int i = m-1; i >= 0; --i)
    {
        if (op[i][0] == ‘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 0;
}

时间: 2024-08-05 03:48:29

ACM学习历程—SNNUOJ 1110 传输网络((并查集 && 离线) || (线段树 && 时间戳))(2015陕西省大学生程序设计竞赛D题)的相关文章

ACM学习历程—SNNUOJ 1110 A Simple Problem(递推 &amp;&amp; 逆元 &amp;&amp; 组合数学 &amp;&amp; 快速幂)(2015陕西省大学生程序设计竞赛K题)

Description Assuming a finite – radius “ball” which is on an N dimension is cut with a “knife” of N-1 dimension. How many pieces will the “ball” be cut into most?However, it’s impossible to understand the following statement without any explanation.L

snnu(1110) 传输网络 (并查集+路径压缩+离线操作)

1110: 传输网络 Time Limit: 3 Sec  Memory Limit: 512 MBSubmit: 43  Solved: 18[Submit][Status][Web Board] [Edit] Description Byteland国家的网络单向传输系统可以被看成是以首都Bytetown为中心的有向树,一开始只有Bytetown建有基站,所有其他城市的信号都是从Bytetown传输过来的.现在他们开始在其他城市陆续建立了新的基站,命令“C x“代表在城市x建立了一个新的基站

angry_birds_again_and_again(2014年山东省第五届ACM大学生程序设计竞赛A题)

http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2877 题目描述 The problems called "Angry Birds" and "Angry Birds Again and Again" has been solved by many teams in the series of contest in 2011 Multi-University Tr

ZZUOJ-1195-(郑州大学第七届ACM大学生程序设计竞赛E题)

1195: OS Job Scheduling Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 106  Solved: 35 [Submit][Status][Web Board] Description OS(Operating System) is to help user solve the problem, such as run job(task).A multitasking OS is one that can simultaneo

ZZUOJ-1195-OS Job Scheduling(郑州大学第七届ACM大学生程序设计竞赛E题)

1195: OS Job Scheduling Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 106  Solved: 35 [Submit][Status][Web Board] Description OS(Operating System) is to help user solve the problem, such as run job(task).A multitasking OS is one that can simultaneo

ACM学习历程—SNNUOJ 1239 Counting Star Time(树状数组 &amp;&amp; 动态规划 &amp;&amp; 数论)

http://219.244.176.199/JudgeOnline/problem.php?id=1239 这是这次陕西省赛的G题,题目大意是一个n*n的点阵,点坐标从(1, 1)到(n, n),每个点都有权值,然后从(x, y)引x轴的垂线,然后构成一个三角形,三个顶点分别是(0, 0),(x, 0)和(x, y).求三角形内点的权值和,包括边界,n的范围是1000,m的范围是100000,说起来也比较坑..学弟n*m的复杂度竟然水过去了,目测比赛数据比较水..不过我挂到我们OJ上给了一组随

ACM学习历程—Hihocoder 1291 Building in Sandbox(dfs &amp;&amp; 离线 &amp;&amp; 并查集)

http://hihocoder.com/problemset/problem/1291 前几天比较忙,这次来补一下微软笔试的最后一题,这题是这次微软笔试的第四题,过的人比较少,我当时在调试B题,没时间看这一题.不过打过之前一场BestCoder的应该都会有点思路,虽然BC那题是二维,这题是三维的,但是思路应该是一样的,没错,就是离线加并查集. 正过来考虑的时候,发现第一个要求相邻块是好处理的,但是第二个要求能否到达(1000, 1000, 1000)这个条件似乎比较难判断,当时BC上的题根据题

山东省第一届ACM大学生程序设计竞赛(原题) 回顾 4.18

Phone Number 题目链接:http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=2151&cid=1172 题意很简单:给出N行电话号码,寻找有没有一串是另一串的前缀,有的话输出No,当然两个一样的也是No 题解:没有前缀0,直接用二维数组存,大循环就行了,用strcmp比较相等.不会超时. Hello World!     题目链接:http://acm.sdut.edu.cn/sdutoj/showproblem.php?pid=215

[简单思维题]Sequence(山东省第九届ACM大学生程序设计竞赛E题)

Problem Description We define an element a_iai? in a sequence "good", if and only if there exists a j(1\le j < i)j(1≤j<i) such that a_j < a_iaj?<ai?.Given a permutation pp of integers from 11 to nn. Remove an element from the permuta