数据结构(树巨结构)
题目1 ShadowIterator与啦啦 操汉子
问题描述
ShadowIterator狂拍啦啦操妹子的照片引起了啦啦操汉子的愤怒,他们吧ShadowIterator引诱到了一个迷宫中准备杀死他!!!
这个迷宫是个有向图,并且时时刻刻都在发生变化。每次变化时,首先是两个节点u,v消失了,并新生成一个节点,新节点的编号是之前出现过的节点数+1;然后原本从u或v出发的边都变成有新节点出发,指向u或v的边都指向新节点。
ShadowIterator很快被这个变化的搞得晕头转向,迷失了方向。他在迷宫里面胡乱的走来走去,因为不知道下一秒迷宫会变成什么样子,所以他完全不知道该向哪个方向走。当他不知道下一步该去哪里的时候他就会向迷宫外的你提问,每次提问是否有从节点u指向v的边,你需要快速回答他的每个问题。
ShadowIterator是死是活就完全靠你了,你作为信息学竞赛的选手,自然能够救出ShadowIterator,不是吗?
输入格式
第一行三个整数N,M,Q,表示这个有向图一开始有N个节点,编号从1到N,有M条有向边,以及Q次操作。
接下来M行,每行两个整数u,v,表示一开始有一条从u指向v的有向边,可能有重边,可能有自环。
接下来Q行每行三个数k,u,v,
·如果k=0表示迷宫发生了变化,u,v节点消失并形成了一个新节点;
·如果k=1表示ShadowIterator向你提问是否有从u指向v的有向边,保证u和v节点都存在。
输出格式
对每次提问输出一行,如果有输出Yes,如果没有输出No。
样例输入
5 7 8
1 2
2 3
3 4
4 5
5 1
1 3
1 4
1 1 4
1 2 5
0 3 5
1 2 6
1 6 4
1 6 2
0 6 2
1 4 7
样例输出
Yes
No
Yes
Yes
No
Yes
提示
样例解释:
一开始迷宫如图1,第三次操作后迷宫如图2,第七次操作后迷宫如图3。(重边只画出了一条)
数据范围:
1<=N<=100000
1<=M<=300000
1<=Q<=200000
题解
一道好题...可以积累下来这种方法
首先考虑离线做。对于一个新点,是由两个点组合而来的,那么就将这个新点向这两个点连边,这样最后连出来是一个森林,求了dfs序之后发现,组成某个点的某些点在dfs序上的编号一定是连续的,所以可以考虑数据结构,现在要求的是u是否有边连向v也就是组成u的某个点右边连向组成v的点(可能有多个),于是就是两端区间求是否右边,可以将其抽象到坐标轴上,有一个点(x,y)表示在dfs序上的编号为x的点在原图上有向dfs序上编号为y的点连边。这样两段区间就成了一个矩形,求这个矩形(包括边上)是否包含点。然后有很巧妙地用一种方法:求到一个矩形的上边界与下边界,然后将它们与点按纵坐标排序,如果有多个点或边在同一纵坐标上,那么就按照下界最先,端点第二,上边界最后排序。然后一个个遍历,用树状数组维护x轴即可
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 100003;
const int MAXQ = 200003;
struct node{
int y , k , x[2] , num;
friend bool operator < ( node a , node b ){
if( a.y != b.y ) return a.y < b.y;
return a.k < b.k;
}
}s[700003];
int op[300003] , u[300003] , v[300003] , dfn[MAXN+MAXQ];
int n ,m ,q , ncnt , L[MAXN+MAXQ] , R[MAXN+MAXQ] , inde , tot;
vector<int>G[MAXN+MAXQ];
bool flag[MAXN+MAXQ];
int ans[MAXQ] , tre[MAXN+MAXQ];
inline void dfs( int x ){
dfn[x] = ++inde;
L[x] = inde;
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
dfs( v );
}
R[x] = inde;
}
inline int lowbit( int x ) { return x & -x;}
inline void modify( int x , int delta){
for( ; x <= ncnt ; x += lowbit( x ) )
tre[x] += delta;
}
inline int query( int x , int y ){
int tot = 0;
for( ; y ; y -= lowbit( y ) ){
tot += tre[y];
}
for( ; x ; x -= lowbit( x) )
tot -= tre[x];
return tot;
}
int main(){
scanf( "%d%d%d" , &n , &m , &q );
for( int i = 1 ; i <= m ; i ++ ){
scanf( "%d%d" , &s[i].x[0] , &s[i].y );
s[i].k = 1;
}
ncnt = n;tot = m;
for( int i = 1 ; i <= q ; i ++ ){
scanf( "%d%d%d" , &op[i] , &u[i] , &v[i] );
if( op[i] == 0 ){
ncnt ++;
G[ncnt].push_back( u[i] );
G[ncnt].push_back( v[i] );
}
}
for( int i = ncnt ; i >= 1 ; i -- ){
if( !L[i] )
dfs( i );
}
for( int i = 1 ; i <= m ; i ++ ){
s[i].x[0] = s[i].x[1] = dfn[s[i].x[0]];
s[i].y = dfn[s[i].y];
}
for( int i = 1 ; i <= q ; i ++ ){
if( op[i] == 1 ){
s[++tot].x[0] = L[u[i]] , s[tot].x[1] = R[u[i]];
s[tot].k = 0 , s[tot].y = L[v[i]];
s[tot].num = i;
s[++tot].x[0] = L[u[i]] , s[tot].x[1] = R[u[i]];
s[tot].k = 2 , s[tot].y = R[v[i]];
s[tot].num = i;
}
}
sort( s + 1 , s + tot + 1 );
for( int i = 1 ; i <= tot ; i ++ ){
if( s[i].k == 1 ){
modify( s[i].x[0] , 1 );
}
else if( s[i].k == 0 ){
ans[s[i].num] = query( s[i].x[0] - 1 , s[i].x[1] );
}
else
ans[s[i].num] = query( s[i].x[0] - 1 , s[i].x[1] ) - ans[s[i].num];
}
for( int i = 1 ; i <= q ; i ++ ){
if( op[i] == 1 ){
if( ans[i] )
printf( "Yes\n" );
else
printf( "No\n" );
}
}
return 0;
}
对于大多数人应该可以切掉吧
题目2 部分可持久化动态树
数据范围:
保证文件中出现的所有数字(除n、m外)都为非负整数并且<=10000。
题解
考虑离线,发现直接倍增即可,记录一下每条边的时间戳, 然后判断是否已经大于t即可
那么现在强制在线,怎么办呢?直接选择递归求倍增数组即可
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 100003;
int n , m , b;
int f[MAXN][23] , g[MAXN][23];
bool flag[MAXN][23];
void dfs( int x , int y ){
if( flag[x][y] ) return ;
if( !y ) return ;
dfs( x , y - 1 );
if( flag[x][y-1] ){
dfs( f[x][y-1] , y - 1 );
if( flag[f[x][y-1]][y-1] ){
f[x][y] = f[f[x][y-1]][y-1];
g[x][y] = max( g[x][y-1] , g[f[x][y-1]][y-1] );
flag[x][y] = 1;
}
}
}
int main(){
scanf( "%d%d%d" , &n , &m , &b );
int lastans = 0;
for( int i = 1 ; i <= m ; i++ ){
int op , u ,v , k;
scanf( "%d" , &op );
op = ( op + lastans * b % 3) % 3;
if( !op ){
scanf( "%d%d" , &u , &v );
u = ( u + lastans * b % n ) % n + 1;
v = ( v + lastans * b % n ) %n + 1;
f[u][0] = v;
g[u][0] = i;
flag[u][0] = 1;
}
else if( op == 1 ){
int t;
scanf( "%d%d%d" , &t , &u , &k );
t = ( t + lastans * b % m ) % m , u = ( u + lastans * b % n ) % n + 1;
k = ( k + lastans * b %n ) % n;
for( int j = 20 ; j >= 0 && u != 0 ; j -- ){
if( ( 1 << j) <= k ){
dfs( u , j );
if( f[u][j] > 0 && g[u][j] <= t )
u = f[u][j] , k -= ( 1 << j );
else
u = 0;
}
}
printf( "%d\n" , u );
lastans = u;
}
else{
int t;
scanf( "%d%d" , &t , &u );
t = ( t + lastans * b % m ) % m;
u = ( u + lastans * b % n ) % n + 1;
int sum = 0;
for( int j = 20 ; j >= 0 ; j -- ){
dfs( u , j );
if( f[u][j] > 0 && g[u][j] <= t ){
u = f[u][j] , sum += ( 1 << j );
}
}
printf( "%d\n" , sum );
lastans = sum;
}
}
return 0;
}
时间复杂度O(N)