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;
}
小结:计数题技巧

浙公网安备 33010602011771号