题解 P3950 【部落冲突】

· · 题解

大家都用log^2的树状数组+树剖?那我来讲一下离线吧!

【和NOIP2016Day1T2有点类似】

首先,我们把问题抽象化一下:一棵带权树,权值一开始都是0,三种操作,1)把某条边权改为1 2)把某条边权改为0 3)求出u到v之间路径上的边权和是否为0

怎么做呢?显然可以LCT,然而题主懒得码模板。

注意到u到v之间路径上的边权和=(1到u路径上的边权和)+(1到v路径上的边权和)-2*(1到LCA(u,v)路径上的边权和)

于是我们把1个query拆成了3个query,接下来的问题就是,如何离线处理6*10^5个形如对“在第T个时间单位1到v路径上的边权和“的查询

我们跑一遍dfs,dfs的时候保存一棵线段树,维护当前节点v在所有时间上到根节点1的边权和。

从v的父亲转化到v,只需要进行关于v的父亲与v之间的战争的modify;考虑完子树v后回到v的父亲,也只需要进行同等数量反向的modify。(其实是djq不会码可持久化啦)

好吧djq蒟蒻讲不清楚,贴代码吧(一样丑)

#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <cmath>
#include <cstdio>
#include <cctype>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cfloat>
#include <ciso646>
#include <climits>
#include <clocale>
#include <complex>
#include <csetjmp>
#include <csignal>
#include <cstdarg>
#include <cstddef>
#include <cwchar>
#include <cwctype>
#include <exception>
#include <locale>
#include <numeric>
#include <new>
#include <stdexcept>
#include <limits>
#include <valarray>
#include <set>
#include <vector>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <list>
#include <utility>
#include <bitset>
#include <algorithm>
#include <functional>
#define rep(i,n) for(int i=0;i<(n);i++)
#define rep1(i,n) for(int i=1;i<=(n);i++)
#define MP make_pair
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int INF=1e9+7;
//segment tree
int tree[1048576];
int query(int id){
    id+=524288;
    int ans=tree[id];
    id>>=1;
    while(id>0){
        ans+=tree[id];
        id>>=1;
    }
    return ans;
}
void modify(int l,int r,int v){
    l+=524288;r+=524288;
    while(l<r){
        if(l&1)tree[l]+=v;
        if(!(r&1))tree[r]+=v;
        l=(l+1)>>1;
        r=(r-1)>>1;
    }
    if(l==r)tree[l]+=v;
}
//
vector<int> G[300005];
vector<pii> qy[300005];
vector<pair<pair<pii,pii>,pii> > Q;
vector<pii> war[300005];
int dep[300005],pre[300005][20];
// lca_part
void init_dfs(int v,int par){
    dep[v]=dep[par]+1;
    pre[v][0]=par;
    rep(k,G[v].size()){
        int u=G[v][k];
        if(u!=par)init_dfs(u,v);
    }
}
void init_lca(int n){
    rep1(i,18)rep1(j,n)
    pre[j][i]=pre[pre[j][i-1]][i-1];
}
int LCA(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    int dif=dep[u]-dep[v];
    rep(k,19)if(dif>>k&1)u=pre[u][k];
    if(u==v)return u;
    for(int k=18;k>=0;k--)
    if(pre[u][k]!=pre[v][k]){
        u=pre[u][k];
        v=pre[v][k]; 
    }
    return pre[u][0];
}
//main part
void dfs(int v,int par){
    rep(k,war[v].size())
    modify(war[v][k].first,war[v][k].second,1);
    rep(k,qy[v].size())qy[v][k].second=query(qy[v][k].first);
    rep(k,G[v].size()){
        int u=G[v][k];
        if(u==par)continue;
        dfs(u,v);
    }
    rep(k,war[v].size())
    modify(war[v][k].first,war[v][k].second,-1);
}
vector<pii> w;
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,q;
    cin>>n>>q;
    rep(k,n-1){
        int u,v;
        cin>>u>>v;
        G[u].push_back(v);
        G[v].push_back(u);
    }
    init_dfs(1,0);
    init_lca(n);
    rep(k,q){
        char t;
        int u,v;
        cin>>t;
        if(t=='C'){
            cin>>u>>v;
            if(dep[u]<dep[v])swap(u,v);
            w.push_back(MP(u,k));
        }else if(t=='U'){
            cin>>u;u--;
            war[w[u].first].push_back(MP(w[u].second,k));
            w[u].second=-1;
        }else{
            cin>>u>>v;int uv=LCA(u,v);
            Q.push_back(MP(MP(MP(u,qy[u].size()),MP(v,qy[v].size())),MP(uv,qy[uv].size())));
            qy[u].push_back(MP(k,-1));qy[v].push_back(MP(k,-1));qy[uv].push_back(MP(k,-1));
        }
    }
    rep(k,w.size())if(w[k].second!=-1)war[w[k].first].push_back(MP(w[k].second,q-1));
    dfs(1,0);
    rep(k,Q.size())cout<<(qy[Q[k].first.first.first][Q[k].first.first.second].second+
                          qy[Q[k].first.second.first][Q[k].first.second.second].second==
                          2*qy[Q[k].second.first][Q[k].second.second].second?"Yes\n":"No\n");
    return 0;
}