线段树及其扩展

板子:单点修改+区间查询

#include<bits/stdc++.h>
using namespace std;
struct Tree
{
    int l,r;
    int dat;
    #define l(x) tree[x].l;
    #define r(x) tree[x].r;
    #define d(x) tree[x].dat;
}tree[N*4];
void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {d(p)=a[p];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1+1,mid+1,r);
    d(p)=max(d(p<<1),d(p<<1+1));
}
void change(int p,int x,int v)
{
    if(l(p)==r(p)) {d(p)=v;return;}
    int mid=(l(p)+r(p))>>1;
    if(x<=mid) change(p<<1,x,v);
    else       change(p<<1+1,x,v);
    d(p)=max(d(p<<1),d(p<<1+1));
}
int ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return d(p);
    int mid=(l(p)+r(p))>>1;
    int val=-(1<<30);
    if(mid>=l) val=max(val,ask(p<<1,l,r));
    if(mid+1<=r) val=max(val,ask(p<<1+1,l,r));
    return val;
}
int main()
{
    build(1,1,n);
    change(1,x,v);
    ask(1,1,n);
} 
View Code

 

T1:求最大连续子段和(单点修改)

//第一次认真的打线段树的板子....
//第一次拼命wrong的点竟然在建树是的赋值,醉了醉了... 
#include<bits/stdc++.h>
using namespace std;
const int N=500010;
int n,T,c[N];
struct Tree
{
    int sum,dat,lmax,rmax,l,r;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define s(x) t[x].sum
    #define d(x) t[x].dat
    #define lm(x) t[x].lmax
    #define rm(x) t[x].rmax
}t[N*4];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void push(int p)
{
    s(p)=s(p<<1)+s((p<<1)+1);
    lm(p)=max(lm(p<<1),s(p<<1)+lm((p<<1)+1));
    rm(p)=max(rm((p<<1)+1),s((p<<1)+1)+rm(p<<1));
    d(p)=max(max(d(p<<1),d((p<<1)+1)),rm(p<<1)+lm((p<<1)+1));
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {s(p)=d(p)=lm(p)=rm(p)=c[l(p)];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build((p<<1)+1,mid+1,r);
    push(p);
}
inline void change(int p,int x,int v)
{
    if(l(p)==r(p)) {s(p)=d(p)=lm(p)=rm(p)=v;return;}
    int mid=(l(p)+r(p))>>1;
    if(x<=mid) change(p<<1,x,v);
    else       change((p<<1)+1,x,v);
    push(p);
}
inline Tree ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return t[p];
    int mid=(l(p)+r(p))>>1;
    if(r<=mid) return ask(p<<1,l,r);
    else if(l>=mid+1) return ask((p<<1)+1,l,r);
    else 
    {
        Tree a,b,c;
        a=ask(p<<1,l,r);
        b=ask((p<<1)+1,l,r);
        c.sum=a.sum+b.sum;
        c.lmax=max(a.lmax,a.sum+b.lmax);
        c.rmax=max(b.rmax,b.sum+a.rmax);
        c.dat=max(max(a.dat,b.dat),a.rmax+b.lmax);
        return c;
    }
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();T=read();
    for(register int i=1;i<=n;++i) c[i]=read();
    build(1,1,n);
    while(T--)
    {
        int op=read(),x=read(),y=read();
        if(op==1) 
        {
            if(x>y) swap(x,y);
            printf("%d\n",ask(1,x,y).dat);
        }
        else if(op==2) change(1,x,y);
    } 
    return 0;
View Code

 

板子:区间修改+区间查询.

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=100010;
int n,m,a[N];
struct Tree
{
    int l,r;
    ll sum,add;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define add(x) t[x].add
}t[N*4];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void push(int p)
{
    if(add(p))
    {
        sum(p<<1)+=(r(p<<1)-l(p<<1)+1)*add(p);
        sum((p<<1)+1)+=(r((p<<1)+1)-l((p<<1)+1)+1)*add(p);
        add(p<<1)+=add(p);
        add((p<<1)+1)+=add(p);
        add(p)=0;
    }
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {sum(p)=a[l];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build((p<<1)+1,mid+1,r);
    sum(p)=sum(p<<1)+sum((p<<1)+1);
} 
inline void change(int p,int l,int r,ll d)
{
    if(l<=l(p)&&r>=r(p)) 
    {
        sum(p)+=d*(r(p)-l(p)+1);
        add(p)+=d;
        return; 
    }
    push(p);
    int mid=(l(p)+r(p))>>1;
    if(mid>=l) change(p<<1,l,r,d);
    if(mid+1<=r) change((p<<1)+1,l,r,d);
    sum(p)=sum(p<<1)+sum((p<<1)+1); 
}
inline ll ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return sum(p);
    ll val=0;
    push(p);
    int mid=(l(p)+r(p))>>1;
    if(mid>=l) val+=ask(p<<1,l,r);
    if(mid+1<=r) val+=ask((p<<1)+1,l,r);
    return val;
} 
int main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();
    for(register int i=1;i<=n;++i) a[i]=read();
    build(1,1,n); 
    while(m--)
    {
        int op=read();
        if(op==1)
        {
            int x=read(),y=read(),k=read();
            change(1,x,y,k);
        }
        else 
        {
            int x=read(),y=read();
            printf("%lld\n",ask(1,x,y));
        }
    }
    return 0;
} 
View Code

T2:

区间修改(+和*)+区间查询;

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=500010;
int n,m,P,a[N];
struct Tree
{
    int l,r;
    ll sum,add,mul;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define add(x) t[x].add
    #define mul(x) t[x].mul
}t[N*4];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void push(int p)
{
    if(mul(p)!=1)
    {
        sum(p<<1)=(sum(p<<1)*mul(p))%P;
        sum((p<<1)+1)=(sum((p<<1)+1)*mul(p))%P;
        add(p<<1)=(add(p<<1)*mul(p))%P;
        add((p<<1)+1)=(add((p<<1)+1)*mul(p))%P;
        mul(p<<1)=(mul(p<<1)*mul(p))%P;
        mul((p<<1)+1)=(mul((p<<1)+1)*mul(p))%P;
        mul(p)=1;
    }
    if(add(p))
    {
        sum(p<<1)=(sum(p<<1)+(r(p<<1)-l(p<<1)+1)*add(p))%P;
        sum((p<<1)+1)=(sum((p<<1)+1)+(r((p<<1)+1)-l((p<<1)+1)+1)*add(p))%P;
        add(p<<1)=(add(p<<1)+add(p))%P;
        add((p<<1)+1)=(add((p<<1)+1)+add(p))%P;
        add(p)=0;
    }
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;mul(p)=1;
    if(l==r) {sum(p)=a[l];return;}
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build((p<<1)+1,mid+1,r);
    sum(p)=(sum(p<<1)+sum((p<<1)+1))%P; 
}
inline void change1(int p,int l,int r,ll d)
{
    if(l<=l(p)&&r>=r(p)) 
    {
        sum(p)=(sum(p)*d)%P;
        add(p)=(add(p)*d)%P;
        mul(p)=(mul(p)*d)%P;
        return;
    }
    push(p);
    int mid=(l(p)+r(p))>>1;
    if(l<=mid) change1(p<<1,l,r,d);
    if(r>=mid+1) change1((p<<1)+1,l,r,d);
    sum(p)=(sum(p<<1)+sum((p<<1)+1))%P;
}
inline void change2(int p,int l,int r,ll d)
{
    if(l<=l(p)&&r>=r(p))
    {
        sum(p)=(sum(p)+(r(p)-l(p)+1)*d)%P;
        add(p)=(add(p)+d)%P;
        return;
    }
    push(p);
    int mid=(l(p)+r(p))>>1;
    if(l<=mid) change2(p<<1,l,r,d);
    if(r>=mid+1) change2((p<<1)+1,l,r,d);
    sum(p)=(sum(p<<1)+sum((p<<1)+1))%P;
}
inline ll ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return sum(p);
    push(p);
    int mid=(l(p)+r(p))>>1;
    ll val=0;
    if(l<=mid) val=(val+ask(p<<1,l,r))%P;
    if(r>=mid+1) val=(val+ask((p<<1)+1,l,r))%P;
    return val;
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();m=read();P=read();
    for(register int i=1;i<=n;++i) a[i]=read();
    build(1,1,n);
    while(m--)
    {
        int op=read();
        if(op==1) 
        {
            int x=read(),y=read(),k=read();
            change1(1,x,y,k);
        }
        else if(op==2)
        {
            int x=read(),y=read(),k=read();
            change2(1,x,y,k);
        }
        else if(op==3)
        {
            int x=read(),y=read();
            printf("%lld\n",ask(1,x,y));
        }
    }
    return 0;
} 
View Code

 T3:区间最大公约数(单点修改)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=500010;
ll n,T,a[N],c[N],b[N];
struct Tree
{
    ll l,r,dat;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define d(x) t[x].dat
}t[N*4];
inline ll read()
{
    ll x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
ll lowbit(ll x) {return x&(-x);}
inline void add(ll x,ll k) {for(;x<=n;x+=lowbit(x)) b[x]+=k;}
inline ll assk(ll x)
{
    ll ans=0;
    for(;x;x-=lowbit(x)) ans+=b[x];
    return ans;
}
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
inline void build(ll p,ll l,ll r)
{
    l(p)=l,r(p)=r;
    if(l==r) {d(p)=c[l];return;}
    ll mid=(l+r)>>1;
    build(p<<1,l,mid);
    build((p<<1)+1,mid+1,r);
    d(p)=gcd(d(p<<1),d((p<<1)+1));
} 
inline void change(ll p,ll x,ll v)
{
    if(x>n) return;
    if(l(p)==r(p)) {d(p)+=v;return;}
    ll mid=(l(p)+r(p))>>1;
    if(x<=mid) change(p<<1,x,v);
    else       change((p<<1)+1,x,v);
    d(p)=gcd(d(p<<1),d((p<<1)+1));
}
inline ll ask(ll p,ll l,ll r)
{
    if(l>r) return 0;     
    if(l<=l(p)&&r>=r(p)) return abs(d(p));
    ll mid=(l(p)+r(p))>>1;
    ll vl=0,vr=0;
    if(l<=mid) vl=ask(p<<1,l,r);
    if(r>=mid+1) vr=ask((p<<1)+1,l,r);
    return abs(gcd(vl,vr));
}
int main()
{
    freopen("1.in","r",stdin);
    n=read();T=read();
    for(register int i=1;i<=n;++i) a[i]=read(),c[i]=a[i]-a[i-1];
    build(1,1,n);
    while(T--)
    {
        char ch;
        cin>>ch;if(ch=='C')
        {
            ll x=read(),y=read(),v=read();
            change(1,x,v);add(x,v);
            change(1,y+1,-v);add(y+1,-v);
        }
        else if(ch=='Q')
        {
            ll x=read(),y=read();
            printf("%lld\n",gcd(a[x]+assk(x),ask(1,x+1,y)));
        }
    }
    return 0;
} 
View Code

 4:扫描线:

 

 

 

 

 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
double Y[N*2],x1,yy1,x2,y2;
ll n,cnt;
struct ScanLine
{
    double l,r,x;
    int mark;
}line[N*2];
struct Tree
{
    int l,r,sum;
    double len;
    #define l(p) t[p].l
    #define r(p) t[p].r
    #define sum(p) t[p].sum
    #define len(p) t[p].len
}t[N<<3];
inline bool cmp(ScanLine a,ScanLine b){return a.x<b.x;}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    sum(p)=len(p)=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build((p<<1)+1,mid+1,r);
}
inline void push(int p)
{
    if(sum(p)) len(p)=Y[r(p)+1]-Y[l(p)];
    else       len(p)=len(p<<1)+len((p<<1)+1); 
}
inline void change(int p,double l,double r,int c)
{
    if(l<=Y[l(p)]&&r>=Y[r(p)+1]) 
    {
        sum(p)+=c;
        push(p);
        return;
    }
    ll mid=(l(p)+r(p))>>1;
    if(Y[mid]>=l) change(p<<1,l,r,c);
    if(Y[mid+1]<r) change((p<<1)+1,l,r,c);
    push(p);
}
int main()
{
    freopen("1.in","r",stdin);
    while(cin>>n)
    {
        cnt++;
        if(n==0) break;
        for(register int i=1;i<=n;++i)
        {
            scanf("%lf %lf %lf %lf",&x1,&yy1,&x2,&y2);
            Y[i*2]=yy1;Y[i*2-1]=y2;
            line[i*2]=(ScanLine){yy1,y2,x1,1};
            line[i*2-1]=(ScanLine){yy1,y2,x2,-1};
        }
        n<<=1;
        sort(line+1,line+n+1,cmp);
        sort(Y+1,Y+n+1);
        int tot=unique(Y+1,Y+n+1)-Y-1;
        build(1,1,tot-1); 
        double ans=0;
        for(register int i=1;i<n;++i)
        {
            change(1,line[i].l,line[i].r,line[i].mark);
            ans+=len(1)*(line[i+1].x-line[i].x);
        }
        printf("Test case #%d\n",cnt);
        printf("Total explored area: %.2lf\n\n",ans);
    }
    return 0;
}
View Code

 

最后记得一定要将i枚举到n,为了将线段树信息抵消...

最近又做了巨多线段树的题:

.....上次做完做鸡蛋后就对延迟标记的了解更深了....

这个就是修改还行,直接就全部覆盖,至于每次查询,它问什么我们就求什么,我们保存每个区间的三个信息,区间内的最大连续空位,区间最靠左的连续空位,区间最靠右的连续空位.

至于更新信息嘛就很简单了.最难的时查询时,我们一次考虑构成答案的三种情况.sum(p<<1),那就地柜左子树,sum(p<<1|1)你就递归右子树.rsum(p<<1)+lsum(p<<1|1)那就直接算答案.由于答案要求我们最小,所以我们搜索顺序就要调换一下...

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
int n,m;
struct Tree
{
    int l,r,lsum,rsum,sum,add;
    #define lsum(x) t[x].lsum
    #define rsum(x) t[x].rsum
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define add(x) t[x].add
}t[N<<2];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void pushup(int p)
{
    lsum(p)=lsum(p<<1);
    if(lsum(p<<1)==r(p<<1)-l(p<<1)+1) lsum(p)+=lsum(p<<1|1);
    rsum(p)=rsum(p<<1|1);
    if(rsum(p<<1|1)==r(p<<1|1)-l(p<<1|1)+1) rsum(p)+=rsum(p<<1);
    sum(p)=max(max(sum(p<<1),sum(p<<1|1)),rsum(p<<1)+lsum(p<<1|1));
}
inline void pushdown(int p)
{   
    if(add(p)==1)
    {
        sum(p<<1)=lsum(p<<1)=rsum(p<<1)=r(p<<1)-l(p<<1)+1;
        sum(p<<1|1)=lsum(p<<1|1)=rsum(p<<1|1)=r(p<<1|1)-l(p<<1|1)+1;
        add(p<<1)=1;
        add(p<<1|1)=1;
        add(p)=0;
    }
    else if(add(p)==-1)
    {
        sum(p<<1)=lsum(p<<1)=rsum(p<<1)=0;
        sum(p<<1|1)=lsum(p<<1|1)=rsum(p<<1|1)=0;
        add(p<<1)=-1;
        add(p<<1|1)=-1;
        add(p)=0;
    }
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {sum(p)=rsum(p)=lsum(p)=1;return;}
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}
inline void alter(int p,int l,int r,int op)
{
    if(l<=l(p)&&r>=r(p))
    {
        if(op==1)
        {
            sum(p)=lsum(p)=rsum(p)=r(p)-l(p)+1;
            add(p)=1;
        }
        else 
        {
            sum(p)=lsum(p)=rsum(p)=0;
            add(p)=-1;
        }
        return;
    }
    pushdown(p);
    int mid=l(p)+r(p)>>1;
    if(l<=mid) alter(p<<1,l,r,op);
    if(r>=mid+1) alter(p<<1|1,l,r,op);
    pushup(p);
}
inline int ask(int p,int x)
{
    if(l(p)==r(p)) return l(p);
    pushdown(p);
    int mid=l(p)+r(p)>>1;
    if(sum(p<<1)>=x) return ask(p<<1,x);
    else if(rsum(p<<1)+lsum(p<<1|1)>=x) return mid-rsum(p<<1)+1;
    else if(sum(p<<1|1)>=x) return ask(p<<1|1,x);
}
int main()
{
    n=read();m=read();
    build(1,1,n);
    for(int i=1;i<=m;++i)
    {
        int op=read();
        if(op==1)
        {
            int x=read();
            if(sum(1)>=x)
            {
               int s=ask(1,x);
               printf("%d\n",s);
               alter(1,s,s+x-1,-1);
            }
            else printf("0\n");
        }
        else 
        {
            int x=read(),y=read();
            alter(1,x,x+y-1,1);
        }
    }
    return 0;
}
View Code

 

疯狂的在推式子,结果需要暴力修改.

因为每次的开跟,所以1e9最多开5次不变了,所以其实大部分的修改都是无效的.我们修改时就暴力修改子树,当子树修改时已经无影响时,我们打上标记,下次再修改时直接return即可...

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=1e5+10;
int n,a[N],m;
struct Tree
{
    int l,r;
    ll sum;
    bool vis;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define vis(x) t[x].vis
}t[N<<2];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) 
    {
        sum(p)=a[l];
        if(sum(p)==0||sum(p)==1) vis(p)=1;
        return;
    }
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    if(vis(p<<1)&&vis(p<<1|1)) vis(p)=1;
}
inline ll ask(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return sum(p);
    int mid=l(p)+r(p)>>1;
    ll ans=0;
    if(l<=mid) ans+=ask(p<<1,l,r);
    if(r>=mid+1) ans+=ask(p<<1|1,l,r);
    return ans;
}
inline void alter(int p,int l,int r)
{
    if(l(p)==r(p)) 
    {
        sum(p)=sqrt(sum(p));
        if(sum(p)==0||sum(p)==1) vis(p)=1;
        return;
    }
    if(vis(p)) return;
    int mid=r(p)+l(p)>>1;
    if(l<=mid) alter(p<<1,l,r);
    if(r>=mid+1) alter(p<<1|1,l,r);
    sum(p)=sum(p<<1)+sum(p<<1|1);
    if(vis(p<<1)&&vis(p<<1|1)) vis(p)=1;
}
int main()
{
    n=read();
    for(int i=1;i<=n;++i) a[i]=read();
    build(1,1,n);
    m=read();
    for(int i=1;i<=m;++i)
    {
        int op=read(),x=read(),y=read();
        if(op==1) printf("%lld\n",ask(1,x,y));
        else      alter(1,x,y);
    }
    return 0;
}
View Code

好吧,最后几道就压轴出场了:

这道题真的想写很久了,前置知识一学完就又回来刚这道题...

不过真的咋看不会,看的第一眼,嗯,树上差分,如果z值不大,那我们就可以轻松用数组搞定,之后用dfs合并输出,每次在一个点的cnt[x][z]中选出最大的一个点,这样的复杂度...emmm应该是(nz)的.不过不光是时间过不去,空间也开不下....

之后我就翻开题解,一看他们全部都是权值线段树啊,啥主席树啊,在哪瞎搞,权值线段树我也懂,可为什么要用呢?我是真的不懂.....死劲看....也看不出来....

终于我好像懂了点....权值线段树用数值存次数,简单地说就是一个桶...就像我们以前用的桶排序一样,既然数组开不下,我们完全可以用权值线段树代替,而且查询最大值还是log的

其实仔细想想,没错啊!权值线段树不就只是用权值记录次数吗?我们要找的最大值不也是找所有的数中出现次数最多的吗?之后,就用权值线段树瞎搞....

在合并时用上线段树合并...玄学复杂度....于是乎就这样水过了....不过最后wrong的一个点着实让人不满意...提醒我们权值线段树当出现次数为负值时,不可更新答案...

怎么说呢....真的很高兴.....哈哈哈哈哈哈哈哈.....

#include<bits/stdc++.h>
using namespace std;
const int N=100010,M=10000010;
int n,m,link[N],tot,root[N],cnt;
int deep[N],f[N][25],fa[N];
struct edge{int y,next;}a[N<<1];
struct Tree
{
    int lc,rc,dat,mx;
    #define lc(x) t[x].lc
    #define rc(x) t[x].rc
    #define dat(x) t[x].dat
    #define mx(x) t[x].mx
}t[M<<2];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void add(int x,int y)
{
    a[++tot].y=y;
    a[tot].next=link[x];
    link[x]=tot;
}
inline void bfs()
{
    queue<int>q;q.push(1);
    deep[1]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(int i=link[x];i;i=a[i].next)
        {
            int y=a[i].y;
            if(deep[y]) continue;
            deep[y]=deep[x]+1;q.push(y);
            f[y][0]=x;fa[y]=x;
            for(int i=1;i<=20;++i) f[y][i]=f[f[y][i-1]][i-1];
         }
    }
}
inline int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    for(int i=20;i>=0;--i) 
        if(deep[f[x][i]]>=deep[y]) x=f[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;--i)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];        
}
inline int build()
{
    cnt++;
    lc(cnt)=rc(cnt)=dat(cnt)=0;
    return cnt;
}
inline void pushup(int p)
{
    if(mx(lc(p))>=mx(rc(p))){mx(p)=mx(lc(p));dat(p)=dat(lc(p));}
    else{mx(p)=mx(rc(p));dat(p)=dat(rc(p));}
}
inline void alter(int p,int l,int r,int v,int k)
{
    if(l==r) {mx(p)+=k;if(mx(p)>0) dat(p)=l;return;}
    int mid=l+r>>1;
    if(v<=mid)
    {
        if(!lc(p)) lc(p)=build();
        alter(lc(p),l,mid,v,k);
    }
    else 
    {
        if(!rc(p)) rc(p)=build();
        alter(rc(p),mid+1,r,v,k);
    }
    pushup(p);
}
inline int merge(int p,int q,int l,int r)
{
    if(!p) return q;
    if(!q) return p;
    if(l==r)
    {
        mx(p)+=mx(q);
        if(mx(p)>0) dat(p)=l;
        return p;
    }
    int mid=l+r>>1;
    lc(p)=merge(lc(p),lc(q),l,mid);
    rc(p)=merge(rc(p),rc(q),mid+1,r);
    pushup(p);
    return p;
}
inline void dfs(int x)
{
    for(int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa[x]) continue;
        dfs(y);
        root[x]=merge(root[x],root[y],1,N-1);
    }
//    printf("x=%d dat(root[1])=%d\n",x,dat(root[1]));
}
int main()
{
    //freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<n;++i)
    {
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    bfs();
    for(int i=1;i<=n;++i) root[i]=build();
    for(int i=1;i<=m;++i)
    {
        int x=read(),y=read(),z=read();                                
        int d=lca(x,y);                                    
        alter(root[x],1,N-1,z,1);                            
        alter(root[y],1,N-1,z,1);                          
        alter(root[d],1,N-1,z,-1);                               
        alter(root[fa[d]],1,N-1,z,-1);                        
    }
    dfs(1);
    for(int i=1;i<=n;++i) printf("%d\n",dat(root[i]));
    return 0;
} 
View Code

这个题,有一说一,确实是树剖的好题....

则么说呢,感觉线段树维护的信息很显然..就是不知道讲树上的节点划分成若干个链后怎么统计答案....

想不通....那只能在每次跳到fa[top[a]]时统计答案。

具体的我们可以将这条链结束时,和即将跳的点颜色比较,因为这些节点就是这些链的分开的节点.....

感觉和区间某些性质相关的题都可以这么做,就是在每次跳时都比较和父亲的属性是否相同.

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,b[N],link[N],tot;
int deep[N],fa[N],top[N],wson[N],dfn[N],pre[N],size[N],m1;
struct edge{int y,next;}a[N<<1];
struct Tree
{
    int l,r,sum,lc,rc,lazy;
    #define l(x) t[x].l
    #define r(x) t[x].r
    #define sum(x) t[x].sum
    #define lc(x) t[x].lc
    #define rc(x) t[x].rc
    #define lazy(x) t[x].lazy
}t[N<<2];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void add(int x,int y)
{
    a[++tot].y=y;
    a[tot].next=link[x];
    link[x]=tot;
}
inline void pushup(int p)
{
    sum(p)=sum(p<<1)+sum(p<<1|1);
    if(rc(p<<1)==lc(p<<1|1)) sum(p)--;
    lc(p)=lc(p<<1);rc(p)=rc(p<<1|1);
}
inline void push(int p)
{
    if(lazy(p))
    {
        sum(p<<1)=1;
        sum(p<<1|1)=1;
        lc(p<<1)=rc(p<<1)=lazy(p);
        lc(p<<1|1)=rc(p<<1|1)=lazy(p);
        lazy(p<<1|1)=lazy(p<<1)=lazy(p);
        lazy(p)=0; 
    } 
}
inline void build(int p,int l,int r)
{
    l(p)=l,r(p)=r;
    if(l==r) {sum(p)=1;lc(p)=rc(p)=b[pre[l]];return;}
    int mid=l+r>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    pushup(p);
}
inline void dfs1(int x,int father)
{
    size[x]=1;
    for(int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==father) continue;
        fa[y]=x;deep[y]=deep[x]+1;
        dfs1(y,x);    
        size[x]+=size[y];
        if(size[y]>size[wson[x]]) wson[x]=y;
    }
}
inline void dfs2(int x,int tp)
{
    top[x]=tp;
    dfn[x]=++m1;
    pre[m1]=x;
    if(wson[x]) dfs2(wson[x],tp);
    for(int i=link[x];i;i=a[i].next)
    {
        int y=a[i].y;
        if(y==fa[x]||y==wson[x]) continue;
        dfs2(y,y);
    }
}
inline void Tree_alter(int p,int l,int r,int k)
{
    if(l<=l(p)&&r>=r(p))
    {
        sum(p)=1;
        lc(p)=rc(p)=k;
        lazy(p)=k;
        return;
    }
    push(p);
    int mid=l(p)+r(p)>>1;
    if(l<=mid) Tree_alter(p<<1,l,r,k);
    if(r>=mid+1) Tree_alter(p<<1|1,l,r,k);
    pushup(p); 
}
inline int Tree_query(int p,int l,int r)
{
    if(l<=l(p)&&r>=r(p)) return sum(p);
    push(p);
    int ans=0,cnt=0; 
    int mid=l(p)+r(p)>>1;
    if(l<=mid) ans+=Tree_query(p<<1,l,r),cnt++;
    if(r>=mid+1) ans+=Tree_query(p<<1|1,l,r),cnt++;
    if(cnt==2&&rc(p<<1)==lc(p<<1|1)) ans--;
    return ans; 
}
inline void alter(int a,int b,int k)
{
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        Tree_alter(1,dfn[top[a]],dfn[a],k);
        a=fa[top[a]];
    }
    if(deep[a]<deep[b]) swap(a,b);
    Tree_alter(1,dfn[b],dfn[a],k);
}
inline int ask(int p,int x)
{
    if(l(p)==r(p)) return lc(p);
    push(p);
    int mid=l(p)+r(p)>>1;
    if(x<=mid) return ask(p<<1,x);
    else       return ask(p<<1|1,x);
}
inline int query(int a,int b)
{
    int ans=0;
    while(top[a]!=top[b])
    {
        if(deep[top[a]]<deep[top[b]]) swap(a,b);
        ans+=Tree_query(1,dfn[top[a]],dfn[a]);
        if(fa[top[a]]!=0&&ask(1,dfn[top[a]])==ask(1,dfn[fa[top[a]]])) ans--;
        a=fa[top[a]];
     }
     if(deep[a]<deep[b]) swap(a,b);
     ans+=Tree_query(1,dfn[b],dfn[a]);
    return ans; 
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i) b[i]=read();
    for(int i=1;i<n;++i)
    {
        int x=read(),y=read();
        add(x,y);add(y,x);
    }
    dfs1(1,0);dfs2(1,1);
    build(1,1,n);
    for(int i=1;i<=m;++i)
    {
        char c;cin>>c;
        if(c=='Q') 
        {
            int x=read(),y=read();
            printf("%d\n",query(x,y));
        }
        else 
        {
            int x=read(),y=read(),k=read();
            alter(x,y,k);
        }
    }
    return 0;
}
View Code
posted @ 2020-01-01 07:17  逆天峰  阅读(235)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//