Codeforces Round 881 (Div. 3)--F2

发布时间 2023-06-22 19:19:46作者: zhujio

F2. Omsk Metro (hard version)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl "\n"
#define int long long
const int N=2e5+5;
const int INF = 0x3f3f3f3f;
// 假设一个区间的最大字段和为 max 最小字段和为 min
// 那么 [min,max]区间的数都可以取到,即都存在对应的子区间。因为点权仅为 1 或 −1
// 一个子区间左右扩张,其值只会加一或者减一,不会跳跃,而初始值为 0
//后面就非常简单了,直接树剖
std::vector<int>edge[N];
struct op{
    char opr;
    int x,y,v;
}a[N];
int val[N],now_n;
//
int n,v[N],sz[N],son[N],dist[N],father[N];
//求子树大小,重儿子,深度,父亲
void Dfs(int x,int fa){
    sz[x]=1;
    for(auto y:edge[x]){
        if(y==fa)continue;
        dist[y]=dist[x]+1;
        father[y]=x;
        Dfs(y,x);
        sz[x]+=sz[y];
        if(sz[y]>sz[son[x]])son[x]=y;
    }
}
int now_dfn,l_dfn[N],r_dfn[N],pos_dfn[N],top[N];
//对于每个点重新编号,同时维护出重链最顶上的点,还有dfn序
//编号顺序就是求在dfn序是加入一个限制,先走重链(从同一点出发子树大的路径)
void DFS(int x,int fa,int tp){
    l_dfn[x]=++now_dfn;pos_dfn[now_dfn]=x;top[x]=tp;
    if(son[x])DFS(son[x],x,tp);
    for(auto y:edge[x]){
        if(y!=fa&&y!=son[x])DFS(y,x,y);
    }
    r_dfn[x]=now_dfn;
}
struct Node{
    int maxn,l_maxn,r_maxn;
    int minn,l_minn,r_minn;
    int sum;
}Tree[N*4];
void pushup(Node &now,Node &l,Node &r){
    now.sum=l.sum+r.sum;
    now.maxn=max(l.r_maxn+r.l_maxn,max(l.maxn,r.maxn));
    now.minn=min(l.r_minn+r.l_minn,min(l.minn,r.minn));
    now.l_maxn=max(l.l_maxn,l.sum+r.l_maxn);
    now.r_maxn=max(r.r_maxn,r.sum+l.r_maxn);
    now.l_minn=min(l.l_minn,l.sum+r.l_minn);
    now.r_minn=min(r.r_minn,r.sum+l.r_minn);
}
void pushup(int now){
    pushup(Tree[now],Tree[now*2],Tree[now*2+1]);
}
void build(int now,int l,int r){
    if(l==r){
        Tree[now].maxn=Tree[now].l_maxn=Tree[now].r_maxn=val[pos_dfn[l]];
        Tree[now].minn=Tree[now].l_minn=Tree[now].r_minn=val[pos_dfn[l]];
        Tree[now].sum=val[pos_dfn[l]];
        return;
    }
    int mid=(l+r)>>1;
    build(now*2,l,mid);
    build(now*2+1,mid+1,r);
    pushup(now);
}
Node qurey(int now,int l,int r,int x,int y){
    if(x<=l&&y>=r)return Tree[now];
    int mid=(l+r)>>1;
    if(y<=mid)return qurey(now*2,l,mid,x,y);
    if(x>mid)return qurey(now*2+1,mid+1,r,x,y);
    Node ans;
    Node ls=qurey(now*2,l,mid,x,y);
    Node rs=qurey(now*2+1,mid+1,r,x,y);
    pushup(ans,ls,rs);
    return ans;
}
Node calc(int x,int y){
    //这里不能用INT_MAX,INT_MIN 会爆
    Node left={-INF,-INF,-INF,INF,INF,INF,0};
    Node right=left;
    while(top[x]!=top[y]){
        if(dist[top[x]]<dist[top[y]])swap(x,y),swap(left,right);
        Node now=qurey(1,1,now_n,l_dfn[top[x]],l_dfn[x]);
        Node t=left;
        //树链向上跳,显然now的dfn序会更小
        pushup(left,now,t);
        x=father[top[x]];
    }
    if(dist[x]<dist[y])swap(x,y),swap(left,right);
    Node t=left;
    Node now=qurey(1,1,now_n,l_dfn[y],l_dfn[x]);
    pushup(left,now,t);
    swap(left.r_maxn,left.l_maxn);
    swap(left.r_minn,left.l_minn);
    Node ans;
    pushup(ans,left,right);
    return ans;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
    int T=1;cin>>T;
    while(T--){
        cin>>n;
        for(int i=0;i<=n+5;i++)edge[i].clear(),son[i]=0;
        now_n=1;val[1]=1;now_dfn=0;dist[1]=0;
        for(int i=1;i<=n;i++){
            cin>>a[i].opr;
            if(a[i].opr=='+'){
                cin>>a[i].y>>a[i].v;
                edge[a[i].y].push_back(++now_n);
                val[now_n]=a[i].v;
            }
            else cin>>a[i].x>>a[i].y>>a[i].v;
        }
        Dfs(1,1);DFS(1,1,1);
        build(1,1,now_n);
        for(int i=1;i<=n;i++){
            if(a[i].opr=='+')continue;
            int x=a[i].x,y=a[i].y,v=a[i].v;
            if(x==y){
                if(v==0||v==val[x])cout<<"YES"<<endl;
                else cout<<"NO"<<endl;
                continue;
            }
            Node ans=calc(x,y);
            ans.minn=min(0ll,ans.minn);
            ans.maxn=max(0ll,ans.maxn);
            if(v>=ans.minn&&v<=ans.maxn)cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
    }       
    return 0;
}