题解 P5267 【美好的每一天~不连续的存在~】

· · 题解

思路(对我来说)好毒瘤的一道题啊…… %%%lxl %%%比赛时AC的大佬

(以下是我对于正解的理解) 大概来说,是有两次的问题转化的过程:

第一步

建出点分树,这样对于询问(l,r,x)中x所在的一个连通块,必定完全包含在[l,r]区间内某一个节点(在点分树上)的子树里面,在点分树上暴力跳父亲即可找到这个节点,然后把这个询问"送"给这个节点,接下来我们一个节点一个节点地处理这些询问。

这样转化之后,问题就变成了:在一棵树中,从根节点出发,只经过[l,r]范围内的点,可以到达的颜色数。

第二步

我们记录一下每一个节点到(点分树上的)每个祖先的路径上的编号最大的点和编号最小的点,分别设成L和R

我们发现,只有对于x节点拥有的一个询问(l,r),只有L>=l且R<=r的节点才能对答案有贡献

于是,这就是一个二维偏序问题了……将询问离线一波,第一维排序第二维树状数组维护即可解决。

丑陋的不行的代码:

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;

int n,m,xi,yi,zi;
int col[100010],ans[100010],cx[100010];
int nxt[200010],to[200010],head[100010],gs;
int size[100010],used[100010],d[100010];
int rt,srt,tot;

struct node{
    int l,r,id;node(int a=0,int b=0,int c=0){l=a;r=b;id=c;}
    bool operator <(const node d)const{return l==d.l?id>d.id:l>d.l;}
};

vector<node> v[100010],q[100010];

inline void lb(int x,int y){
    nxt[++gs]=head[x];head[x]=gs;to[gs]=y;
}

void frt(int x,int fa){
    int mn=size[x]=1;for(int i=head[x];i;i=nxt[i])
        if(to[i]!=fa && !used[to[i]])frt(to[i],x),size[x]+=size[to[i]],mn=max(mn,size[to[i]]);
    mn=max(mn,tot-size[x]);if(mn<srt)srt=mn,rt=x;
}

void dfs(int x,int fa,int mx,int mn){
    size[x]=1;v[x].push_back(node(mn,mx,rt)),q[rt].push_back(node(mn,mx,col[x]));for(int i=head[x];i;i=nxt[i])
        if(to[i]!=fa && !used[to[i]])dfs(to[i],x,max(mx,to[i]),min(to[i],mn)),size[x]+=size[to[i]];
}

void vp(int x){
    tot=size[x];srt=0x3f3f3f3f;frt(x,-1);used[rt]=1;dfs(x=rt,-1,rt,rt);
    for(int i=head[x];i;i=nxt[i])if(!used[to[i]])vp(to[i]);
}
//以上为蒟蒻的点分树
void update(int x,int v){for(;x<=n;x+=x&-x)d[x]+=v;}
int query(int x,int ans=0){for(;x;x-=x&-x)ans+=d[x];return ans;}
//以上为蒟蒻的树状数组
int main(){
    scanf("%d %d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&col[i]);
    for(int i=1;i<n;i++)scanf("%d %d",&xi,&yi),lb(xi,yi),lb(yi,xi);
    size[1]=n;vp(1);for(int i=1;i<=m;i++){
        scanf("%d %d %d",&xi,&yi,&zi);
        for(vector<node>::iterator id=v[zi].begin();id!=v[zi].end();id++){
            node j=*id;if(j.l>=xi && j.r<=yi){q[j.id].push_back(node(xi,yi,-i));break;}
        }//id为负表示是一个询问
    }for(int i=1;i<=n;i++){
        sort(q[i].begin(),q[i].end());
        for(vector<node>::iterator id=q[i].begin();id!=q[i].end();id++){
            node j=*id;
            if(j.id<0)ans[-j.id]=query(j.r);
            else{
                if(cx[j.id]>j.r)update(cx[j.id],-1),cx[j.id]=0;
                if(!cx[j.id])update(j.r,1),cx[j.id]=j.r;
            }
        }
        for(vector<node>::iterator id=q[i].begin();id!=q[i].end();id++){
            node j=*id;
            if(j.id>0 && cx[j.id]==j.r)update(j.r,-1),cx[j.id]=0;
        }
    }for(int i=1;i<=m;i++)printf("%d\n",ans[i]);return lxl毒瘤;
}