Loading

P8990

P8990
暴力是简单的,树剖+线段树,每一次change都重新做一遍,时间复杂度\(O(nmlog^2n)\)
要求所有操作最多在logn内完成,肯定是要对时间轴上数据结构,猜想ddp,难点在于子树不断变化。
那么接下来肯定只有两个方向

  • 维护边
  • 维护父子关系

先计数问题核心考虑怎么快速check是否合法。白点需要子树,自然想到check黑点,黑点是一个连通块。容易想到点检边容斥。怎么算答案?答案就是黑白边数量
都与边有关,所以考虑第一个方向。那么我们需要维护每个时刻黑点数,黑边数,黑白边数。
可以发现,对于一条边,其过程是黑边->黑白边->白边。取决于两个端点被点亮时间。
可以用线段树解决

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define P pair<ll,ll> 
void rd(ll& x){
    char ch=getchar();
    ll f=1;x=0;
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-48;
        ch=getchar();
    }
    x=x*f;
}
const ll N_ = 5e5;
ll N,M,node,root,ls[N_*4],rs[N_*4],a[N_],T[N_];
struct E{
    ll u,v;
}edge[N_];
struct Node{
    ll sum;//0,1,2 white | white and black | black
    ll mn;//N-i-sum[][2]
    //  tap[3];
    ll cnt;
}t[N_*4];
ll tap[N_*4][3];
inline Node Min(const Node& x,const Node& y){
    Node res;
    if(x.mn<y.mn)return x;
    if(y.mn<x.mn)return y;
    res.mn=x.mn;res.sum=x.sum+y.sum;res.cnt=x.cnt+y.cnt;
    return res;
}
inline void push_up(ll k){
    if(!ls[k]&&!rs[k])return ;
    if(!ls[k])return t[k]=t[rs[k]],void();
    if(!rs[k])return t[k]=t[ls[k]],void();
    t[k]=Min(t[ls[k]],t[rs[k]]);
}
void build(ll& k,ll l,ll r){
    if(!k)k=++node;
    if(l==r)return t[k].mn=1-l,t[k].cnt=1,void();
    ll mid=l+r>>1;
    build(ls[k],l,mid);build(rs[k],mid+1,r);
    push_up(k);
}
inline void push_down(ll k,ll tp){
    ll x=tap[k][tp];
    tap[ls[k]][tp]+=x;
    tap[rs[k]][tp]+=x;
    ll val=x;
    if(tp==1)t[ls[k]].sum+=val*t[ls[k]].cnt,t[rs[k]].sum+=val*t[rs[k]].cnt;
    else t[ls[k]].mn+=val,t[rs[k]].mn+=val;
    tap[k][tp]=0;
}
void add(ll k,ll l,ll r,ll p,ll q,ll tp,ll val){
    if(p<=l&&r<=q){
        if(tp==1)t[k].sum+=val*t[k].cnt;
        else t[k].mn+=val;
        tap[k][tp]+=val;
        return ;
    }
    ll mid=l+r>>1;
    if(tap[k][1]!=0)push_down(k,1);
    if(tap[k][2]!=0)push_down(k,2);
    if(p<=mid)add(ls[k],l,mid,p,q,tp,val);
    if(q>mid)add(rs[k],mid+1,r,p,q,tp,val);
    push_up(k);
}
inline void add(ll u,ll v){
    if(T[u]>T[v])swap(u,v);
    add(root,1,N-1,T[u],T[v]-1,1,1);
    if(T[u]<=N-1)
    add(root,1,N-1,T[u],N-1,2,1);
}
inline void erase(ll u,ll v){
    if(T[u]>T[v])swap(u,v);
    add(root,1,N-1,T[u],T[v]-1,1,-1);
    if(T[u]<=N-1)
    add(root,1,N-1,T[u],N-1,2,-1);
}
int main()
{
    rd(N);rd(M);
    for(int i=1;i<N;i++){
        ll u,v;
        rd(u);rd(v);
        edge[i]={u,v};
    }
    for(int i=1;i<N;i++)rd(a[i]),T[a[i]]=i;
    build(root,1,N-1);
    T[1]=N;
    for(int i=1;i<N;i++){
        ll u=edge[i].u,v=edge[i].v;
        add(u,v);
    }
    if(t[root].mn!=1)cout<<"0\n";
    else cout<<t[root].sum<<'\n';
    while(M--){
        ll x,y;
        rd(x);rd(y);
        erase(x,y);
        rd(x);rd(y);
        add(x,y);
        Node res=t[root];
        if(res.mn!=1)cout<<"0\n";
        else cout<<res.sum<<'\n';
    }
    return 0;
}

小结:计数题技巧

posted @ 2026-08-17 14:49  leaf_ydc  阅读(5)  评论(0)    收藏  举报