您的位置:首页 > 其它

可持久化线段树&主席树、dfs序 Task Highways (aut)

2017-08-12 12:16 183 查看


Highways


Memory limit: 128 MB

Byteland is a small country, with 

 cities
connected by 

 bidirectional roads. From
each city there is just one way of reaching every other city using the road network, which causes severe traffic jams. For this reason several highways have been built; each highway connects some pair of cities.

By a route we mean a sequence of roads and/or highways. All cities on a route should be distinct. For each pair of cities 



 there
exists exactly one route which does not use any highway; we call such a route the main route between 

 and 

.

People going from a city 

 to a city 

 can
either choose the main route or use some highway. In the latter case the route cannot intersect the main route except for the cities 

 and 

 and
must contain exactly one highway.

Your task is to calculate the number of routes people can take between given pairs of cities.


Input

The first line of input contains a single integer 

 (

)
- the number of cities in Byteland. Cities are numbered from 

 to 

.
Each of the next 

 lines contains two
integers 



 (

)
meaning that cities 

 and 

 are
connected by a road.

The next line contains an integer 

 (

)
- the number of highways. Each of the next 

 lines
contains a description of a single highway. The next line contains an integer 

 (

)
- the number of queries. Each of the next 

 lines
contains a description of a query. Both highways and queries are given in the same format as the roads.


Output

Your program should output exactly 

 lines.
The 

-th line should contain the number
of routes in the 

-th query.


Example

For the input data:
9
1 2
2 3
4 2
1 5
5 6
7 5
7 8
9 7
4
2 5
3 4
6 4
8 3
4
4 9
2 5
1 6
1 7


the correct result is:
1
4
2
2

题目链接:https://szkopul.edu.pl/problemset/problem/m_aRcpzjNPTWOyC-0EyGPaap/site/?key=statement
题意:给出一个含有n个节点的树,再给出m条额外边。

询问:u,v之间有多少条路径,这些路径满足下面的条件

(1)这条路径不经过额外边
(2)这条路径不经过树上u,v路径之间的边,并且恰好经过一条额外边。

题解:

这道题在网上没找到什么题解,我就自己写一个啦~

u,v之间树上的路径只有一条,这很容易。我们的重点在于怎么来求第二种路径。

我们先画一个这样的图



可以想到,不能经过u到v之间的路径,那么我们很直观的感受就是u不能往上走了,v也不能往上走了,因为如果往上走的话,势必会经过u,v之间在树上的边。

那么问题就转化成,求连接U和V的子树的额外边有多少条!

关键点1:子树->想到用dfs序,因为dfs序以后,一颗子树相当于是一段连续的区间。

到这里我们显然应该考虑用某种数据结构来维护这个关系。

U的子树是一段连续的区间,而V的子树也是一段连续的区间,让你求连接两个区间的额外边的个数。

主席树模板题嘛。

对于每一条额外边,设额外边的两个端点为a和b。a在左子树,b在右子树,相当于把在a版本的线段树的b位置+1。

最后回答询问的时候,就直接用主席树查询两个版本线段树在某个区间统计值之差就好了。

要注意,在建立主席树的时候,一定要从子节点出发构建,一步步往上,按照dfs序优先的顺序建立,这样才能保证一个子树的对应的版本的线段树是一个连续的区间。

对于每个节点,在dfs建立主席树的时候,用start表示这个节点形成的子树的第一个版本的线段树所在的版本号,用end表示这个节点形成的子树的最后一个版本的线段树所在的版本号。

然后回答询问就直接是下面一句话就可以了。

int ans = seg.query(start[u],last[u],IN[v],OUT[v]);

这题到这里就完了?

Naive!

我们目前只考虑了u,v形成的路径是折线的形式,如果他们在树的一条链上的时候,情况完全不同。

如下图:



这种情况的时候,就不能简单的用U、V的子树来表示了。

但是我们可以考虑一下容斥的想法。假设U、V这条链上的U的儿子为P

那么显然从V的子树出发的额外边,如果连接到P及其子树的任何一个点的时候,那么这条边都是没用的,因此我们可以用做差的形式来得到答案。

也就是V的子树和1的子树之间额外边的数量减去V的子树和P的子树之间额外边的数量,这就是答案。

int ans = seg.query(start[v],last[v],IN[1],OUT[1]) -  seg.query(start[v],last[v],IN[P],OUT[P]);
现在问题来了,怎么求P。

有两种办法

1》从v出发,不断往上回溯父亲节点,直到找到P

2》从U出发,遍历U的孩子,找满足下面这个关系的孩子,那么就是P

IN[vv] <= IN[P] && IN[P] <= OUT[vv]


这里要注意,第一种解法会TLE,因为一个一个向上回溯太慢了!我就是在这里TLE了好多次

到这里,这道题基本是可以做了。

附一张我的曲折历程:



代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef pair<int,int> pii;
const int MAXN = 200100;
int head[MAXN];
int cnt;
struct edge{
int v;
int next;
int cost;
}Es[MAXN<<1];
struct segtree{
int root[MAXN * 30];
int val[MAXN * 30];
int lson[MAXN * 30];
int rson[MAXN * 30];
int idx;
int n;
void init(int N){
n = N;
idx = 0;
memset(val,0,sizeof(root));
memset(val,0,sizeof(val));
memset(lson,0,sizeof(lson));
memset(rson,0,sizeof(rson));
build(root[0],1,n);
}
void build(int& rt,int l,int r){
rt = ++idx;
int mid = (l + r) / 2;
if(l < r) build(lson[rt],l,mid),build(rson[rt],mid+1,r);
}
void insert(int num,int& rt,int l,int r){
int nrt = ++idx;
lson[nrt] = lson[rt];
rson[nrt] = rson[rt];
val[nrt] = val[rt] + 1;
rt = nrt;
if(l == r) return ;
int mid = (l + r) / 2;
if(num <= mid) insert(num,lson[rt],l,mid);
else insert(num,rson[rt],mid+1,r);
}
int _query(int rt,int l,int r,int ul,int ur){
if(r < ul || l > ur) return 0;
if(ul <= l && r <= ur) return val[rt];
int mid = (l + r) / 2;
int a = _query(lson[rt],l,mid,ul,ur);
int b = _query(rson[rt],mid+1,r,ul,ur);
return a + b;
}
void putone(int i,int num){
/*将一个新元素num插入到主席树中,形成第i个版本的线段树*/
root[i] = root[i-1];
insert(num,root[i],1,n);
}
int query(int ul,int ur,int uul,int uur){
/*询问版本区间ul-ur之间的,位于uul到uur之间的数的个数*/
int a = _query(root[ul-1],1,n,uul,uur);
int b = _query(root[ur],1,n,uul,uur);
return b - a;
}
}seg;
void init(){
cnt = 0;
memset(head,-1,sizeof(head));
}
inline void add_edge(int i,int j,int cost){
Es[cnt].v = j;
Es[cnt].cost = cost;
Es[cnt].next = head[i];
head[i] = cnt++;
}
int getint() {
int x = 0, flag = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar())
if (ch == '-') flag = 1;
for(; isdigit(ch); ch = getchar())
x = x * 10 + ch - '0';
return flag ? -x : x;
}
int n,m,q;
int IN[MAXN],OUT[MAXN],dep[MAXN],pa[MAXN];
int idx = 0;
vector<int> G[MAXN];
int sorts[MAXN];
int scnt = 0;
int start[MAXN],last[MAXN];
//int Gcnt[MAXN];
void dfs(int u,int fa){
IN[u] = ++idx;
pa[u] = fa;
for(int e = head[u];e != -1;e = Es[e].next){
int v = Es[e].v;
if(v != fa){
dfs(v,u);
}
}
OUT[u] = ++idx;
//sorts[scnt++] = u;
}int tot = 0;

void dfs2(int u){

for(int e = head[u];e != -1;e = Es[e].next){
int v = Es[e].v;
if(v == pa[u]) continue;
dfs2(v);
if(!start[u]) start[u] = start[v];
if(last[v]) last[u] = last[v];
}
vector<int> ::iterator it = G[u].begin();
for(;it != G[u].end();++it){
int v = *it;
seg.putone(++tot,IN[v]);
if(!start[u]) start[u] = tot;
last[u] = tot;
}
}
int main(){
init();
n = getint();
for(int i = 1;i <= n-1;i++){
int a,b;
a = getint();b = getint();
add_edge(a,b,1);
add_edge(b,a,1);
}
seg.init(2*n);
dfs(1,0);
m = getint();
for(int i = 1;i <= m;i++){
int a,b;
a = getint();b = getint();
G[a].push_back(b);
G[b].push_back(a);
}
dfs2(1);
q = getint();
while(q--){
int u,v;
u = getint();
v = getint();
if(u == v){
printf("%d\n",1);
continue;
}
/*这里需要分情况讨论*/
/* type: ^ */
if(IN[u] > OUT[v] || IN[v] > OUT[u]){
int ans = seg.query(start[u],last[u],IN[v],OUT[v]);
printf("%d\n",ans + 1);
}
/* type: / */
else{
if(IN[u] > IN[v]) swap(u,v);
//makesure that v is in the subtree of u
/*
目测这种做法会TLE
int son = v;
while(pa[son] != u) son = pa[son];
*/
int son;
for(int e = head[u];e != -1;e = Es[e].next){
int vv = Es[e].v;
if(vv != pa[u]){
if(IN[vv] <= IN[v] && IN[v] <= OUT[vv]){
son = vv;
break;
}
}
}
int ans = seg.query(start[v],last[v],IN[1],OUT[1]) -  seg.query(start[v],last[v],IN[son],OUT[son]);
printf("%d\n",ans + 1);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: