Gty的妹子树,洛谷P2137,树上分块+主席树(坑)

正题

      这题其实在思路上不难,但是时间却非常卡人。

      首先我们看到加点,我们就知道要用分块来解决这个问题,在分块的方式上选择size分块。

      然后对于每一个建好的块,建一棵主席树来维护信息。

      改权值的时候,我们就需要先把先前的权值的那条链都加上-1,再把新的权值的那条链加上1.

      然后再加点时,就根据父亲来看一下,是加入父亲的那个块还是自己新开一个块。

      前面dfs建树也是这样。

      求有多少个>k的时候就直接块内暴力,儿子块遍历,注意不能直接通过块与块之间的边来遍历,因为不能保证所遍历到的块在子树内。

      应该在遍历子树的时候,如果遍历到一个不同块,那么就去这个块的整体,二分答案。

      最后ans输出。

      这样0操作的时间复杂度是sqrt(n)*log2(n).

      而1操作和2操作的时间复杂度都是log2(n)。所以应该是比整块暴力排序要快很多的。

      但是我被T飞了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#define INF 2147483647
using namespace std;

int n;
int sqrtn;
int ls[26000010],rs[26000010];
int tot[26000010];
int f[60010];
int root[60010];
struct edge{
    int y,next;
}s[120010];
edge news[120010];
int id[60010];
int first[60010];
int fir[60010];
int d[60010];
int m,tt=0,op,sum[60010],ll=0,len=0;
long long v;

void ins(int x,int y){//原边 
    len++;
    s[len].y=y;s[len].next=first[x];first[x]=len;
}

void inss(int x,int y){
    ll++;
    news[ll].y=y;news[ll].next=fir[x];fir[x]=ll;
}

inline char nc() {
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}

#define getchar() nc()

void read(int &to){
    to=0;
    char ch=getchar();
    int f=1;
    while(ch<'0' || ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9') to=to*10+ch-'0',ch=getchar();
    to*=f;
}

void update(int &now,long long l,long long r){//主席树更新动态建点 
    if(now==0) now=++tt;
    tot[now]+=op;
    if(l==r) return ;
    long long mid=(l+r)/2;
    if(v<=mid) update(ls[now],l,mid);
    else update(rs[now],mid+1,r);
}

int num=0;

void dfs(int x){//更新x点的所在块 
    if(sum[id[f[x]]]==sqrtn) {
    	sum[id[x]=++num]=1;
        inss(id[f[x]],id[x]);
    }
    else sum[id[x]=id[f[x]]]++;
    v=d[x];op=1;
    update(root[id[x]],0,INF);
    for(int i=first[x];i!=0;i=s[i].next){
        int y=s[i].y;
        if(y!=f[x]){
            f[y]=x;
            dfs(y);
        }
    }
}

int ans=0;

void findal(int x,int tar){//块搜索,二分答案 
    for(int i=fir[x];i!=0;i=news[i].next)
        findal(news[i].y,tar);
    long long l=0,r=INF;
    int now=root[x];
    if(tar<0){
    	ans+=tot[now];
        return ;
    }
    while(l<r){
        long long mid=(l+r)/2;
        if(tar<=mid) {
            ans+=tot[rs[now]];
            now=ls[now];
            r=mid;
        }
        else{
            now=rs[now];
            l=mid+1;
        }
    }
}

void findso(int now,int y){//寻找那些与x同块的
    if(d[now]>y) ans++;
    for(int i=first[now];i!=0;i=s[i].next)
        if(s[i].y!=f[now]){
        	if(id[s[i].y]==id[now]) findso(s[i].y,y);
            else findal(id[s[i].y],y);
        }
}

int get_kth(int x,int y){
    ans=0;
    findso(x,y);
    return ans;
}

void change(int x,int y){//改权值
    v=d[x];op=-1;
    update(root[id[x]],0,INF);
    v=d[x]=y;op=1;
    update(root[id[x]],0,INF);
}

void add(int x,int y){//加上一个点
    n++;
    d[n]=y;
    f[n]=x;
    ins(x,n);
    dfs(n);//直接dfs更新 
}

int main(){
    read(n);
    for(int i=1;i<=n-1;i++){
        int x,y;
        read(x);read(y);
        ins(x,y);ins(y,x);
    }
    for(int i=1;i<=n;i++)
        read(d[i]);
    read(m);
    sqrtn=sqrt(n)*log2(n);
    dfs(1);//从1点开始更新儿子
    int type;
    int x,y;
    int lastans=0;
    while(m--){
        read(type);read(x);read(y);
        x^=lastans;y^=lastans;
        if(type==0){
            lastans=get_kth(x,y);
            printf("%d\n",lastans);
        }//分类讨论0
        else if(type==1) change(x,y);//1
        else if(type==2) add(x,y);//2
    }
}


猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/80981507