1110: 传输网络

Time Limit: 3 Sec  Memory Limit: 512 MB
Submit: 43  Solved: 18
[Submit][Status][Web Board] [Edit]

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

HINT

 

Source

2015陕西省大学生程序设计竞赛

题目大意:给定一个关系树,有两种操作,一个是询问这个关系树的根节点,一个是将这个关系树分割。

可以使用并查集,由于关系树可能退化成一条链,每次查询时可能会查询整个链,考虑路径压缩,然而后面可能会有c操作,

对于这种问题,一大堆的询问操作和分割操作,相互交叉,类似于之前做过的一道题,求一段区间中相异数之和,全部读入查询操作,

进行编号,然后依次回答以结尾的查询操作,再根据编号回答,离线操作就是映射了一遍,这道题假设读入所有的c操作,建立一棵

终极树,貌似不能回答没有进行c操作之前的查询,不可能吗?

我们可以倒着做,回答最后一个c之后的查询,然后将这棵树还原到c之前的状态,再次回答,以此类推,将树的状态还原到初始状态,

对每一个状态我们都可以进行带有路压缩的查询了,降低了时间复杂度。

#include <iostream>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
using namespace std;
#define maxn 110000
int n,m;
int q[maxn],c[maxn]; //c数组存的是从开始到结束基站的编号
int father[maxn]; //基站之前的信号发射点
int uset[maxn];
int Rank[maxn];
int answer[maxn]; //从末尾到起始的Q的答案
vector < int > G[maxn];
int cnum;
int MM;
void init()
{
cnum=; MM=;
memset(q,,sizeof(q));
memset(c,,sizeof(c));
memset(father,,sizeof(father));
memset(answer,,sizeof(answer));
for(int i=;i<maxn;i++)
G[i].clear();
}
void MakeSet()
{
for(int i=;i<=n;i++)
{
uset[i]=i;
Rank[i]=;
}
}
int Find1(int x)
{
if(x!=uset[x]) Find1(uset[x]);
return uset[x];
}
void unionSet(int x,int y)
{
int fx=Find1(x);
int fy=Find1(y);
if(fx!=fy)
{
if(Rank[fx]>Rank[fy])
uset[fy]=fx;
if(Rank[fx]<Rank[fy])
uset[fx]=fy;
if(Rank[fx]==Rank[fy])
{
uset[fx]=fy;
Rank[fx]++;
}
}
}
int Find2(int x)
{
if(x!=uset[x]) uset[x]=Find2(uset[x]);
return uset[x];
} void input()
{
for(int i=;i<=n-;i++)
{
int fa,son;
scanf("%d%d",&fa,&son);
uset[son]=fa;
}
for(int i=;i<=m;i++)
{
char s[]; int num;
scanf("%s",s);
if(s[]=='Q')
{
scanf("%d",&num);
G[cnum].push_back(num);
}
if(s[]=='C')
{
scanf("%d",&num);
G[++cnum].push_back(num);
c[cnum]=num;
father[num]=Find1(uset[num]);
uset[num]=num; //分割开
}
}
/*cout<<"cnum "<<cnum<<endl;
for(int i=cnum;i>=0;i--)
{
printf("%d ",G[i].size());
for(int j=G[i].size()-1;j>=0;j--)
{
printf("%d ",G[i][j]);
}
printf("\n");
}*/
} void solve()
{
// cout<<"cnum "<<cnum<<endl;
for(int i=cnum;i>=;i--)
{
if(i==)
{
/*for(int j=G[i].size()-1;j>=1;j--)
{
printf("%d ",G[i][j]);
}
cout<<endl;*/
// cout<<"0 "<<G[i].size()<<endl;00
for(int j=G[i].size()-;j>=;j--)
{
int x=G[i][j];
answer[MM++]=Find2(x);
// cout<<"0 "<<G[i][j]<<" "<<answer[MM-1]<<endl;
}
}
else
{
/* for(int j=G[i].size()-1;j>=1;j--)
{
printf("%d ",G[i][j]);
}
cout<<endl;*/
for(int j=G[i].size()-;j>=;j--)
{
int x=G[i][j];
answer[MM++]=Find2(x);
// cout<<"1 "<<answer[MM-1]<<endl;
}
// cout<<"G[i][0] "<<G[i][0]<<endl;
// cout<<"father[G[i][0]] "<<father[G[i][0]]<<endl;
uset[G[i][]]=father[G[i][]];
}
}
for(int i=MM-;i>=;i--)
printf("%d\n",answer[i]); }
int main()
{
while(~scanf("%d%d",&n,&m))
{
init();
MakeSet();
input();
solve();
}
return ;
}

线段树:

可将每个集合染成同样的颜色,利用前序遍历建立时间戳,然后可得每个节点所管辖的节点,由于查询的是节点的最近祖先, 所以不是单纯的染色操作,

可能会有这种情况出现,先染小区间,再染大区间,如果是普通的染色操作,查询就不符合情况。

在pushdown操作中用颜色标记去更新时,如果之前的颜色标记比现在的标记大,那么就不用更新,因为它问的是最近的祖先。

线段树区间染色:

1:查询操作(统计区间内颜色个数)有其特殊性,只要查询区间包含在当前区间内,并且当前区间有颜色,那么查询区间的颜色与当前区间的颜色相同。

(颜色标记以下的节点都是这个颜色)

2:染色操作,对于标记一般情况下考虑两种情况(之前是否有标记),标记是否可以叠加。

3: pushdown操作,标记向下传递时,用父节点的标记值去更新子节点的val值和标记值。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <vector>
#define LL long long
using namespace std;
int answer=;
//线段树
//区间染色
const int maxN = ;
struct node
{
int lt, rt, val, turn;
}tree[*maxN]; //向下更新
void pushDown(int id)
{
if (tree[id].turn != )
{
tree[id<<].turn = max( tree[id << ].val,tree[id].turn );
tree[id<<].val = tree[id<<].turn ; tree[id<<|].turn = max( tree[id << |].val ,tree[id].turn );
tree[id << |].val= tree[id<< | ].turn; tree[id].turn = ;
}
} //向上更新
void pushUp(int id)
{
if (tree[id<<].val == tree[id<<|].val)
tree[id].val = tree[id<<].val;
else
tree[id].val = ;
} //建立线段树
void build(int lt, int rt, int id)
{
tree[id].lt = lt;
tree[id].rt = rt;
tree[id].val = ;//每段的初值,根据题目要求
tree[id].turn = ;
if (lt == rt)
{
tree[id].turn = ;
tree[id].val = ;
return;
}
int mid = (lt+rt)>>;
build(lt, mid, id<<);
build(mid+, rt, id<<|);
} //更改每段的值,多用于染色
void change(int lt, int rt, int id, int col)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
{
tree[id].val = tree[id].turn = max( tree[id].turn ,col );
return;
}
pushDown(id);
int mid = (tree[id].lt+tree[id].rt)>>;
if (lt <= mid)
change(lt, rt, id<<, col);
if (rt > mid)
change(lt, rt, id<<|, col);
//pushUp(id);
}
/*
void query(int lt, int rt, int id)
{
if (lt > tree[id].rt || rt < tree[id].lt)
return;
if (tree[id].val != 0)
{
answer=tree[id].val;
return;
}
query(lt, rt, id<<1);
query(lt, rt, id<<1|1);
}*/
//查询单点的值
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<<|);
} struct NODE
{
int l,r;
}a[maxN];
typedef vector<int > INT;
vector<INT > Map(maxN);
int number;
void dfs(int fa)
{
for(int i=;i<Map[fa].size();i++)
{
int son=Map[fa][i];
a[son].l= ++number;
dfs(son);
a[son].r=number;
}
}
void init()
{
for(int i=;i<maxN;i++)
Map[i].clear();
}
int main()
{
//freopen("test.txt","r",stdin);
//freopen("out.txt","w",stdout);
int n,m;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=;i<=n-;i++)
{
int from,to;
scanf("%d%d",&from,&to);
Map[from].push_back(to);
}
number=;
a[].l=;
dfs();
a[].r=number; build(a[].l,a[].r,);
// for(int i=1;i<=9;i++)
// printf("%d %d\n",a[i].l,a[i].r);
char str[]; int id;
while(m--)
{
scanf("%s%d",str,&id);
//printf("%s\n",str);
if(str[]=='Q')
{
// answer=1;
answer=query(a[id].l,a[id].l,);
printf("%d\n", answer);
}
if(str[]=='C')
{
change(a[id].l,a[id].r,,id);
}
}
}
return ;
}

snnu(1110) 传输网络 (并查集+路径压缩+离线操作 || 线段树)的更多相关文章

  1. 并查集+路径压缩(poj1988)

    http://poj.org/problem?id=1988 Cube Stacking Time Limit: 2000MS   Memory Limit: 30000K Total Submiss ...

  2. hdu 1558 线段相交+并查集路径压缩

    Segment set Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  3. 【数轴涂色+并查集路径压缩+加速】C. String Reconstruction

    http://codeforces.com/contest/828/problem/C [题意] [思路] 因为题目保证一定有解,所有优化时间复杂度的关键就是不要重复染色,所以我们可以用并查集维护区间 ...

  4. 并查集 + 路径压缩(经典) UVALive 3027 Corporative Network

    Corporative Network Problem's Link Mean: 有n个结点,一开始所有结点都是相互独立的,有两种操作: I u v:把v设为u的父节点,edge(u,v)的距离为ab ...

  5. HDOJ 3635 并查集- 路径压缩,带秩合并

    思路来源:http://blog.csdn.net/niushuai666/article/details/6990421 题目大意: 初始时,有n个龙珠,编号从1到n,分别对应的放在编号从1到n的城 ...

  6. LA 并查集路径压缩

    题目大意:有n个节点,初始时每个节点的父亲节点都不存在.有两种操作 I u v:把点节点u的父亲节点设为v,距离为|u-v|除以1000的余数.输入保证执行指令前u没有父亲节点. E u:询问u到根节 ...

  7. - > 并查集+路径压缩(详解)(第一节)

    先举一个友爱的例子解释一下并查集: 话说江湖上散落着各式各样的大侠,有上千个之多. 他们没有什么正当职业,整天背着剑在外面走来走去,碰到和自己不是一路人的,就免不了要打一架.但大侠们有一个优点就是讲义 ...

  8. PAT甲级1013题解——并查集+路径压缩

    题目分析: 本题初步浏览题目就知道是并查集的模板题,数据输入范围N为1~1000,则M的范围为0~1000^2,通过结构体记录每一对连线的关系,p[]数组记录每个节点的跟,对于k次查询,每次都要重新维 ...

  9. HDU 3635 并查集+路径压缩+记录每个点移动次数

    题意: 给定n个点 oper个操作 每个点有1个龙珠 下面2种操作: T u v 把u点所有龙珠搬到v Q u  问u点当前所在城市 u点所在城市有几个龙珠 u点被移动几次 思路: 并查集可以求出 u ...

随机推荐

  1. apache kafka系列之server.properties配置文件参数说明

    每个kafka broker中配置文件server.properties默认必须配置的属性如下: broker.id=0num.network.threads=2num.io.threads=8soc ...

  2. [NOI2003]Editor(块状链表)

    传送门 看了看块状链表,就是数组和链表的合体. 看上去好高大尚,思想也很简单. 但是发现代码量也不是很小,而且代码理解起来也是费尽得很,倒不如splay用起来顺手. 在加上适用范围貌似不是特别广,所以 ...

  3. jvm参数设置 -vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M

    -vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M 这里有几个问题: 1. 各个参数的含义什么? 2. 为什么有的机器我将- ...

  4. php的错误控制运算符

    php的错误控制运算符 PHP中提供了一个错误控制运算符“@”. 可以将@放置在一个PHP表达式之前,该表达式可能产生的任何错误信息都被忽略掉: 如果开启了php.ini 中的 track_error ...

  5. centos No module named setuptools解决方案

    wget http://pypi.python.org/packages/source/s/setuptools/setuptools-0.6c11.tar.gz tar zxvf setuptool ...

  6. #include<> 和 #include""的区别

    #include< file >编译程序会先到标准函数库中找文件 #include”file” 编译程序会先从当前目录中找文件 参考原文 转: 在C程序中包含文件有以下两种方法: (1)用 ...

  7. 数据库分表和分库的原理及基于thinkPHP的实现方法

    为什么要分表,分库: 当我们的数据表数据量,訪问量非常大.或者是使用频繁的时候,一个数据表已经不能承受如此大的数据訪问和存储,所以,为了减轻数据库的负担,加快数据的存储,就须要将一张表分成多张,及将一 ...

  8. 移动APP怎样保存用户password

    <span style="font-size:14px;">为了更好的用户体验,移动APPclient一般都会将用户信息进行保存以便兴许能够自己主动登录.</sp ...

  9. 给GridView设置行高

    近期在工作中遇到了这样一个问题,使用一个GridView展示数据,item中仅仅是一个TextView,可是里面显示的文字多少不固定多少,必须所有展示出来. 遇到的问题: 1.把item中的宽和高设置 ...

  10. 解决oracle 表被锁住问题

    想修改Oracle下的某一张表,提示 "资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效" 看上去是锁住了. 用系统管理员登录进数据库,然后 SELECT sid, ...