题解 P7394 【「TOCO Round 1」History】
离线操作树 + bfs序 + 树状数组
首先发现这题要求退回到历史版本而未要求强制在线,而且对树的操作也是可逆的,所以可以把所有操作与询问按影响顺序连成一棵操作树。这样每个对每个询问有影响的操作是操作树上的一个前缀。于是我们只需要对操作树进行 dfs,进入节点时执行对应的操作,退出时则撤销操作。
然后考虑询问的特点,如果
然后可以发现一个性质,被拆解后的询问每次涉及的点是原树 bfs 序上连续的一段,具体证明也不难。
然后问题就被转化为了 bfs 序上单点修改区间求和问题,可以用常数小的树状数组解决。
考虑确定询问的区间,可以先把询问离线,用 [Cnoi2019]雪松果树 中的 dfs + 差分 的方法求出询问节点的
总时间复杂度
参考代码(略丑勿喷) :
#include<bits/stdc++.h>
using namespace std;
const int N = 100000;
vector<int> to[200005];
int deep[200005];
int id [200005]; // bfs 序
int stk [200005];
int bin [200005];
int que[200005][3];
int len[200005]; // 区间长度
int lef[200005]; // 时间差
vector<int> ct[200005];
vector<int> q1[200005];
vector<int> q2[200005];
int val[200005];
int ans[200005];
namespace BIT{
int c[100005], N = 100004;
int lowbit( int x ) { return x & -x; }
void modify( int x, int v ) { for( ;x < N; x += lowbit(x) ) c[x] += v; }
int query( int x ) { int r = 0; for( ; x; x -= lowbit(x) ) r += c[x]; return r; }
}
void dfs1( int x, int f ) {
deep[x] = deep[f] + 1; stk[ deep[x] ] = x;
for( auto Q : q1[x] ) if( que[Q][2] < deep[x] )
{ q2[ stk[ deep[x] - que[Q][2] ] ].push_back(Q); }
for( auto N : to[x] ) if( N ^ f ) dfs1( N, x );
}
void dfs2( int x, int f ) {
for( auto Q : q2[x] ) len[Q] -= bin[ deep[x] + que[Q][2] ];
bin[ deep[x] ] ++;
for( auto Q : q1[x] ) lef[Q] = len[Q] + bin[ deep[x] ];
for( auto N : to[x] ) if( N ^ f ) dfs2( N, x );
for( auto Q : q2[x] ) len[Q] += bin[ deep[x] + que[Q][2] ];
}
void dfs3( int x ) {
int y = que[x][1], z = N + x;
if( que[x][0] == 1 ) BIT::modify( id[y], val[y] ? -1 : 1 ), val[y] ^= 1;
if( que[x][0] == 2 ) if( len[x] )
{ ans[x] += BIT::query( id[y] - lef[x] + len[x] ) - BIT::query( id[y] - lef[x] );
ans[x] -= BIT::query( id[y] - lef[z] + len[z] ) - BIT::query( id[y] - lef[z] ); }
for( auto N : ct[x] ) dfs3(N);
if( que[x][0] == 1 ) BIT::modify( id[y], val[y] ? -1 : 1 ), val[y] ^= 1;
}
void bfs() {
queue<int> Q; Q.push(1);
int cnt = 0;
while( Q.size() ) {
int x = Q.front(); Q.pop(); id[x] = ++ cnt;
for( auto N : to[x] ) if( !id[N] ) Q.push(N);
}
}
int main() {
int n, m; cin >> n;
for( int i = 1; i < n; i ++ )
{ int u, v; cin >> u >> v;
to[u].push_back(v);
to[v].push_back(u); }
cin >> m;
for( int i = 1; i <= m; i ++ ) {
cin >> que[i][0];
if( que[i][0] == 1 or que[i][0] == 2 )
{ ct[i - 1].push_back(i); }
if( que[i][0] == 1 ) cin >> que[i][1];
if( que[i][0] == 2 ) cin >> que[i][1] >> que[i][2];
if( que[i][0] == 3 )
{ int k; cin >> k; ct[k].push_back(i); }
if( que[i][0] == 2 ) if( que[i][2] % 2 == 0 )
{ q1[ que[i][1] ].push_back(i);
q1[ que[i][1] ].push_back(i + N);
que[i][2] /= 2;
que[i + N][1] = que[i][1];
que[i + N][2] = que[i][2] - 1; }
}
bfs(); dfs1( 1, 0 ); dfs2( 1, 0 ); dfs3(0);
for( int i = 1; i <= m; i ++ ) if( que[i][0] == 2 ) cout << ans[i] << "\n";
}