主席树不会做

(也许因为人太弱并没有什么对巨佬太难的题

[HNOI/AHOI2017]影魔

设对于一个位置 \(i\) ,它左侧第一个值比它大的位置为 \(l_i\) ,右侧的同理为 \(r_i\) ,分析发现贡献 \(p_1\) 的点对为 \((i,i+1)\)\((l_i,r_i)\) ;贡献 \(p_2\) 的点对为 \(([l_i+1,i-1],r_i)\)\((l_i,[i+1,r_i-1])\) 。查询时即为查询一个正方形内点对贡献。

于是主席树,因为查询横纵坐标一样所以横纵坐标可以互换。拿单点坐标更新区间就好了。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    #define int LL
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=200010;
int n,m,p1,p2,top,l[NN],r[NN],k[NN],stk[NN];
struct TMP{ int l,r,v; };
vector<TMP>ad[NN];

namespace Pers_SegmentTree{
    const int TN=NN<<6;
    int tot,root[NN],lc[TN],rc[TN],tag[TN],sum[TN];
    int clone(int v){
        int u=++tot;
        lc[u]=lc[v]; rc[u]=rc[v]; sum[u]=sum[v]; tag[u]=tag[v];
        return u;
    }
    void update(int& rt,int opl,int opr,int val,int l=1,int r=n){
        rt=clone(rt);
        if(l>=opl&&r<=opr) return tag[rt]+=val,void();
        sum[rt]+=val*(min(opr,r)-max(opl,l)+1);
        int mid=l+r>>1;
        if(opl<=mid) update(lc[rt],opl,opr,val,l,mid);
        if(opr>mid) update(rc[rt],opl,opr,val,mid+1,r);
    }
    int query(int rt,int ls,int opl,int opr,int l=1,int r=n){
        int res=(tag[rt]-tag[ls])*(min(opr,r)-max(opl,l)+1);
        if(l>=opl&&r<=opr) return res+sum[rt]-sum[ls];
        int mid=l+r>>1;
        if(opl<=mid) res+=query(lc[rt],lc[ls],opl,opr,l,mid);
        if(opr>mid) res+=query(rc[rt],rc[ls],opl,opr,mid+1,r);
        return res;
    }
} using namespace Pers_SegmentTree;

signed main(){
    n=read(); m=read(); p1=read(); p2=read();
    for(int i=1;i<=n;i++) k[i]=read(), r[i]=n+1;
    for(int i=1;i<=n;i++){
        while(top&&k[stk[top]]<k[i]) r[stk[top--]]=i;
        l[i]=stk[top]; stk[++top]=i;
    }
    for(int i=1;i<=n;i++){
        if(i<n) ad[i].push_back((TMP){i+1,i+1,p1});
        if(l[i]&&r[i]<=n) ad[l[i]].push_back((TMP){r[i],r[i],p1});
        if(l[i]&&r[i]>i+1) ad[l[i]].push_back((TMP){i+1,r[i]-1,p2});
        if(l[i]<i-1&&r[i]<=n) ad[r[i]].push_back((TMP){l[i]+1,i-1,p2});
    }
    for(int i=1;i<=n;i++){
        root[i]=root[i-1];
        for(TMP x:ad[i]) update(root[i],x.l,x.r,x.v);
    }
    while(m--){
        int l=read(),r=read();
        write(query(root[r],root[l-1],l,r),'\n');
    }
    return 0;
}

[BZOJ4026] dC Loves Number Theory

欧拉函数优美的性质:(以下默认 \(p\) 为素数)

由唯一分解定理将正整数 \(n\) 表示为 \(\prod p_i^{k_i}\) ,那么有 \(\phi(n)=n\times\prod\frac{p_i-1}{p_i}\)

证明:

\(\;\;\;\;\;\;\) 引理: \(\phi(p^k)=p^k-p^{k-1}\) .显然,小于等于 \(p^k\) 的数中除了 \(p^{k-1}\)\(p\) 的倍数,其他都与 \(p\) 互质。

因此有:

\[\begin{align*} \phi(n) &=\phi(\prod p_i^{k_i})\\ &=\prod \phi(p_i^{k_i})\\ &=\prod p_i^{k_i}\times(\frac{p_i-1}{p_i})\\ &=n\times\prod\frac{p_i-1}{p_i} \end{align*} \]

然后主席树即可。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    #define int LL
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int MM=1000010,mod=1e6+777;
int n,q,ans,a[MM],pre[MM],lst[MM],inv[mod+10];

namespace Pers_SegmentTree{
    const int TN=MM<<3;
    int tot,root[MM],lc[TN],rc[TN],sum[TN];
    int clone(int v){
        int u=++tot;
        lc[u]=lc[v]; rc[u]=rc[v]; sum[u]=sum[v];
        return u;
    }
    void build(int& rt,int l=1,int r=n){
        rt=++tot; sum[rt]=1;
        if(l==r) return;
        int mid=l+r>>1;
        build(lc[rt],l,mid);
        build(rc[rt],mid+1,r);
    }
    void insert(int& rt,int pos,int val,int l=1,int r=n){
        rt=clone(rt); (sum[rt]*=val)%=mod;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) insert(lc[rt],pos,val,l,mid);
        else insert(rc[rt],pos,val,mid+1,r);
    }
    int query(int rt,int opl,int opr,int l=1,int r=n){
        if(!rt) return 1;
        if(l>=opl&&r<=opr) return sum[rt];
        int mid=l+r>>1,res=1;
        if(opl<=mid) (res*=query(lc[rt],opl,opr,l,mid))%mod;
        if(opr>mid) (res*=query(rc[rt],opl,opr,mid+1,r))%=mod;
        return res;
    }
    void update(int p,int pos){
        if(lst[p]) insert(root[pos],lst[p],p*inv[p-1]%mod);
        lst[p]=pos; insert(root[pos],pos,(p-1)*inv[p]%mod);
    }
} using namespace Pers_SegmentTree;

signed main(){
    n=read(); q=read(); pre[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=mod;i++) inv[i]=mod-mod/i*inv[mod%i]%mod;
    for(int i=1;i<=n;i++) pre[i]=pre[i-1]*(a[i]=read())%mod;
    build(root[0]);
    for(int i=1;i<=n;i++){
        root[i]=root[i-1];
        for(int j=2;j*j<=a[i];j++) if(a[i]%j==0){
            update(j,i);
            while(a[i]%j==0) a[i]/=j;
        }
        if(a[i]>1) update(a[i],i);
    }
    while(q--){
        int l=read()^ans,r=read()^ans;
        write(ans=(pre[r]*inv[pre[l-1]]%mod*query(root[r],l,r)%mod),'\n');
    }
    return 0;
}

[BZOJ2653] middle

直接求肯定不好求,但可以转化成判定性问题进行二分。

二分区间内最大的 \(mid\) ,使存在至少一中位数 \(x\geq mid\) 。二分结果即最终答案。

对中位数进行转化,令区间内小于 \(mid\) 的数权值为 \(-1\) ,其余权值为 \(1\) ,若区间和不小于 \(0\)\(mid\) 合法。

转化到这道题,对区间赋值后 \([b+1,c-1]\) 一段一定被包含,接下来求 \([a,b],[c,d]\) 两区间左右端点处的最大子段和即可。

对区间赋值的预处理用主席树从小到大逐一将 \(1\) 修改为 \(-1\) 就能实现。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=200010;
int n,q,ans,a[NN],id[NN];
vector<int>Q;

namespace Pers_SegmentTree{
    const int TN=NN<<5;
    int tot,root[NN],lc[TN],rc[TN];
    struct node{
        int sm,mx,lv,rv;
        node(){ sm=mx=lv=rv=0; }
        node(int a){ sm=mx=lv=rv=a; }
        node operator+(const node& t)const{
            node res; res.sm=sm+t.sm;
            res.lv=max(lv,sm+t.lv); res.rv=max(t.rv,rv+t.sm);
            res.mx=max({mx,t.mx,res.lv,res.rv,rv+t.lv});
            return res;
        }
    }s[TN];
    int clone(int v){
        int u=++tot;
        lc[u]=lc[v]; rc[u]=rc[v]; s[u]=s[v];
        return u;
    }
    void build(int& rt,int l=1,int r=n){
        rt=++tot; s[rt]=node(1);
        if(l==r) return;
        int mid=l+r>>1;
        build(lc[rt],l,mid);
        build(rc[rt],mid+1,r);
        s[rt]=s[lc[rt]]+s[rc[rt]];
    }
    void update(int& rt,int pos,node val,int l=1,int r=n){
        rt=clone(rt);
        if(l==r) return s[rt]=val,void();
        int mid=l+r>>1;
        if(pos<=mid) update(lc[rt],pos,val,l,mid);
        else update(rc[rt],pos,val,mid+1,r);
        s[rt]=s[lc[rt]]+s[rc[rt]];
    }
    node query(int rt,int opl,int opr,int l=1,int r=n){
        if(opl>opr) return node(0);
        if(l>=opl&&r<=opr) return s[rt];
        int mid=l+r>>1;
        if(opr<=mid) return query(lc[rt],opl,opr,l,mid);
        if(opl>mid) return query(rc[rt],opl,opr,mid+1,r);
        return query(lc[rt],opl,mid,l,mid)+query(rc[rt],mid+1,opr,mid+1,r);
    }
} using namespace Pers_SegmentTree;

int calc(int l1,int r1,int l2,int r2){
    int l=1,r=n,res;
    while(l<=r){
        int mid=l+r>>1,sum=query(root[mid],r1+1,l2-1).sm;
        sum+=query(root[mid],l1,r1).rv+query(root[mid],l2,r2).lv;
        if(sum>=0) res=mid, l=mid+1;
        else r=mid-1;
    }
    return a[id[res]];
}

signed main(){
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(), id[i]=i;
    sort(id+1,id+n+1,[](int x,int y)->bool{ return a[x]<a[y]; });
    build(root[1]);
    for(int i=2;i<=n;i++)
        update(root[i]=root[i-1],id[i-1],node(-1));
    q=read();
    while(q--){
        for(int i=1;i<5;i++)
            Q.push_back((read()+ans)%n+1);
        sort(Q.begin(),Q.end());
        write(ans=calc(Q[0],Q[1],Q[2],Q[3]),'\n');
        Q.clear();
    }
    return 0;
}

[FJOI2016]神秘数

好像原来做过这种套路的题。。

\(S\) 为当前用来拼数的集合,显然要将数值从小到大加入 \(S\)

\([1,lst]\) 区间内给出的数都用来拼数,拼出了 \([1,res]\) 的连续区间, 那么接下来 \([lst+1,res+1]\) 内的数都能加入 \(S\) ,如果这个区间内没有可用的数,答案即为 \(res+1\)

\(res\) 呈类似斐波那契数列的形式增长,因此直接模拟复杂度没有问题。 求区间内一段值域数字的和,用主席树。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010;
int n,m,a[NN];

namespace Pers_SegmentTree{
    const int TN=NN<<5;
    int tot,root[NN],lc[TN],rc[TN],sum[TN];
    int clone(int v){
        int u=++tot;
        lc[u]=lc[v]; rc[u]=rc[v]; sum[u]=sum[v];
        return u;
    }
    void insert(int& rt,int pos,int l=1,int r=1e9){
        rt=clone(rt); sum[rt]+=pos;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) insert(lc[rt],pos,l,mid);
        else insert(rc[rt],pos,mid+1,r);
    }
    int query(int rt,int ls,int opl,int opr,int l=1,int r=1e9){
        if(l>=opl&&r<=opr) return sum[rt]-sum[ls];
        int mid=l+r>>1,res=0;
        if(opl<=mid) res+=query(lc[rt],lc[ls],opl,opr,l,mid);
        if(opr>mid) res+=query(rc[rt],rc[ls],opl,opr,mid+1,r);
        return res;
    }
} using namespace Pers_SegmentTree;

signed main(){
    n=read();
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=n;i++) insert(root[i]=root[i-1],a[i]);
    m=read();
    while(m--){
        int l=read(),r=read(),res=0,lst=0;
        while("Love is worth years."){
            int tmp=query(root[r],root[l-1],lst+1,res+1);
            if(tmp) lst=res+1, res+=tmp;
            else break;
        }
        write(res+1,'\n');
    }
    return 0;
}

BZOJ3514 GERALD07加强版

个人感觉这题还是挺巧的。

已知 \(n\) 个点,求联通块数,可以求出生成树的树边数,点数减去边数就是答案。

所以 \(LCT\) 维护以编号为权值的最小生成树,再用主席树维护生成树边数。

就是这么简洁,就是想不到~~

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=400010;
int n,m,k,typ,ans,u[NN],v[NN];

namespace LinkCut_Tree{
    int fa[NN],val[NN],mnv[NN],pos[NN],tag[NN],son[NN][2];
    bool get(int x){ return x==son[fa[x]][1]; }
    bool isroot(int x){ return x!=son[fa[x]][0]&&x!=son[fa[x]][1]; }
    void rev(int x){ swap(son[x][0],son[x][1]); tag[x]^=1; }
    void pushdown(int x){ if(tag[x]) rev(son[x][0]), rev(son[x][1]); tag[x]=0; }
    void pushall(int x){ if(!isroot(x)) pushall(fa[x]); pushdown(x); }
    void pushup(int x){
        mnv[x]=min({mnv[son[x][0]],mnv[son[x][1]],val[x]});
        pos[x]=(mnv[x]==val[x]?x:(mnv[x]==mnv[son[x][0]]?pos[son[x][0]]:pos[son[x][1]]));
    }
    void rotate(int x){
        int y=fa[x],z=fa[y],xpos=get(x),ypos=get(y);
        son[y][xpos]=son[x][xpos^1];
        if(son[x][xpos^1]) fa[son[x][xpos^1]]=y;
        if(!isroot(y)) son[z][ypos]=x; fa[x]=z;
        son[x][xpos^1]=y; fa[y]=x;
        pushup(y); pushup(x);
    }
    void splay(int x){
        pushall(x);
        while(!isroot(x)){
            int y=fa[x];
            if(!isroot(y)) get(x)^get(y)?rotate(x):rotate(y);
            rotate(x);
        }
    }
    void access(int x){
        for(int y=0;x;y=x,x=fa[x])
            splay(x), son[x][1]=y, pushup(x);
    }
    void makeroot(int x){ access(x); splay(x); rev(x); }
    void split(int x,int y){ makeroot(x); access(y); splay(y); }
    void link(int x,int y){ makeroot(x); fa[x]=y; }
    void cut(int x,int y){ split(x,y); son[y][0]=fa[x]=0; pushup(y); }
    int findroot(int x){
        access(x); splay(x);
        while(son[x][0]) x=son[x][0];
        splay(x); return x;
    }
} using namespace LinkCut_Tree;

namespace Pers_SegmentTree{
    const int TN=NN<<5;
    int tot,root[NN],lc[TN],rc[TN],sum[TN];
    int clone(int u){
        int p=++tot;
        lc[p]=lc[u]; rc[p]=rc[u]; sum[p]=sum[u];
        return p;
    }
    void insert(int& rt,int pos,int val,int l=1,int r=m){
        rt=clone(rt); sum[rt]+=val;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) insert(lc[rt],pos,val,l,mid);
        else insert(rc[rt],pos,val,mid+1,r);
    }
    int query(int rt,int opl,int opr,int l=1,int r=m){
        if(l>=opl&&r<=opr) return sum[rt];
        int mid=l+r>>1,res=0;
        if(opl<=mid) res+=query(lc[rt],opl,opr,l,mid);
        if(opr>mid) res+=query(rc[rt],opl,opr,mid+1,r);
        return res;
    }
} using namespace Pers_SegmentTree;

signed main(){
    n=read(); m=read(); k=read(); typ=read();
    memset(val,0x3f,sizeof(val)); memset(mnv,0x3f,sizeof(mnv));
    for(int i=1;i<=m;i++) val[n+i]=mnv[n+i]=i;
    for(int i=1;i<=n+m;i++) pos[i]=i;
    for(int i=1;i<=m;i++){
        u[i]=read(); v[i]=read();
        insert(root[i]=root[i-1],i,1-(u[i]==v[i]));
        if(u[i]==v[i]) continue;
        if(findroot(u[i])==findroot(v[i])){
            split(u[i],v[i]); insert(root[i],mnv[v[i]],-1);
            int loc=pos[v[i]]-n;
            cut(loc+n,u[loc]); cut(loc+n,v[loc]);
        }
        link(n+i,u[i]); link(n+i,v[i]);
    }
    while(k--){
        int l=read()^ans*typ,r=read()^ans*typ;
        write(ans=(n-query(root[r],l,r)),'\n');
    }
    return 0;
}


[BZOJ4771]七彩树

其实是好久之前的考试题,当时还没学主席树,稀里糊涂就学着改了。。现在再做一遍,感觉确实很巧。

首先又是 \(LCA\) 的结论:树上任意 \(x\) 个点的 \(LCA\) 集合等价于这 \(x\) 个点中 \(DFS\) 序相邻点对的 \(LCA\)集合。

因为询问与深度有关,所以以深度为根的下标建立主席树,开set记录每个颜色已加入点的 \(DFS\) 序集合,每次加点找到前驱后继,在 \(LCA\)处-1,再在 \(LCA\)\(LCA\)处+1,使颜色数贡献始终正确。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef unsigned long long ULL;
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010;
int t,n,m,ans,maxdep,c[NN];
vector<int>e[NN],node[NN];
set<int>pos[NN];

namespace Pers_SegmentTree{
    const int TN=NN<<8;
    int tot,root[NN],sum[TN],lc[TN],rc[TN];
    int clone(int v){
        int u=++tot;
        sum[u]=sum[v]; lc[u]=lc[v]; rc[u]=rc[v];
        return u;
    }
    void update(int& rt,int pos,int val,int l=1,int r=n){
        rt=clone(rt); sum[rt]+=val;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) update(lc[rt],pos,val,l,mid);
        else update(rc[rt],pos,val,mid+1,r);
    }
    int query(int rt,int opl,int opr,int l=1,int r=n){
        if(l>=opl&&r<=opr) return sum[rt];
        int mid=l+r>>1,res=0;
        if(opl<=mid) res+=query(lc[rt],opl,opr,l,mid);
        if(opr>mid) res+=query(rc[rt],opl,opr,mid+1,r);
        return res;
    }
} using namespace Pers_SegmentTree;

namespace Tree_Chain{
    int cnt,fa[NN],loc[NN],dfn[NN],siz[NN],son[NN],top[NN],dep[NN];
    void dfs1(int s,int f){
        fa[s]=f; siz[s]=1; son[s]=0; dep[s]=dep[f]+1;
        node[dep[s]].push_back(s);
        ckmax(maxdep,dep[s]);
        for(int v:e[s]){
            dfs1(v,s);
            siz[s]+=siz[v];
            if(siz[v]>siz[son[s]]) son[s]=v;
        }
    }
    void dfs2(int s,int t){
        top[s]=t; dfn[s]=++cnt; loc[cnt]=s;
        if(!son[s]) return;
        dfs2(son[s],t);
        for(int v:e[s]) if(v!=son[s]) dfs2(v,v);
    }
    int LCA(int x,int y){
        while(top[x]^top[y])
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
        return dep[x]<dep[y]?x:y;
    }
} using namespace Tree_Chain;

signed main(){
    t=read();
    while(t--){
        n=read(); m=read();
        for(int i=1;i<=n;i++) c[i]=read();
        for(int i=2;i<=n;i++) e[read()].push_back(i);
        dfs1(1,0); dfs2(1,1);
        for(int i=1;i<=maxdep;i++){
            root[i]=root[i-1];
            for(int x:node[i]){
                pos[c[x]].insert(dfn[x]);
                update(root[i],dfn[x],1);
                int pre=(pos[c[x]].find(dfn[x])==pos[c[x]].begin()?0:*--pos[c[x]].find(dfn[x]));
                int nex=(pos[c[x]].find(dfn[x])==--pos[c[x]].end()?0:*++pos[c[x]].find(dfn[x]));
                if(pre) update(root[i],dfn[LCA(loc[pre],x)],-1);
                if(nex) update(root[i],dfn[LCA(loc[nex],x)],-1);
                if(pre&&nex) update(root[i],dfn[LCA(loc[pre],loc[nex])],1);
            }
        }
        while(m--){
            int x=read()^ans,d=read()^ans;
            write(ans=query(root[min(maxdep,dep[x]+d)],dfn[x],dfn[x]+siz[x]-1),'\n');
        }
        for(int i=1;i<=n;i++) e[i].clear(), pos[i].clear();
        for(int i=1;i<=maxdep;i++) node[i].clear();
        ans=tot=cnt=maxdep=0;
    }
    return 0;
}

[BZOJ3221]树上询问

这种题,你说它难吧,它没有任何思维可言;你说它简单吧,它确实还贼ex。。

树剖加主席树加维护等差数列。

线段树维护等差数列时,由于等差数列加等差数列还是等差数列,因此可以维护区间内权值总和与覆盖整个区间的等差数列的首项与公差,在主席树上标记永久化,进行区间维护。

加上树剖后一条链会形成两个公差为相反数的祖先链,要分开处理。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef unsigned long long ULL;
    #define int long long
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010;
int n,m;
LL ans;
char op;
vector<int>e[NN];

namespace Pers_SegmentTree{
    const int TN=NN<<8;
    int tot,mem,now,root[TN],lc[TN],rc[TN];
    LL fst[TN],dif[TN],sum[TN];
    int clone(int rt){
        int now=++tot;
        lc[now]=lc[rt]; rc[now]=rc[rt];
        fst[now]=fst[rt]; dif[now]=dif[rt]; sum[now]=sum[rt];
        return now;
    }
    void update(int& rt,int opl,int opr,int fs,int df,int l=1,int r=n){
        if(opl>opr) return;
        rt=clone(rt);
        if(l==opl&&r==opr){ fst[rt]+=fs; dif[rt]+=df; return; }
        sum[rt]+=(fs*2+df*(opr-opl))*(opr-opl+1)>>1;
        int mid=l+r>>1;
        if(opr<=mid) return update(lc[rt],opl,opr,fs,df,l,mid);
        if(opl>mid) return update(rc[rt],opl,opr,fs,df,mid+1,r);
        update(lc[rt],opl,mid,fs,df,l,mid);
        update(rc[rt],mid+1,opr,fs+df*(mid-opl+1),df,mid+1,r);
    }
    LL query(int rt,int opl,int opr,int l=1,int r=n){
        if(opl>opr) return 0;
        if(!rt) return 0;
        LL res=(fst[rt]*2+dif[rt]*(opr+opl-l*2))*(opr-opl+1)>>1;
        if(l==opl&&r==opr) return res+sum[rt];
        int mid=l+r>>1;
        if(opr<=mid) return res+query(lc[rt],opl,opr,l,mid);
        if(opl>mid) return res+query(rc[rt],opl,opr,mid+1,r);
        return res+query(lc[rt],opl,mid,l,mid)+query(rc[rt],mid+1,opr,mid+1,r);
    }
} using namespace Pers_SegmentTree;

namespace Tree_Chain{
    int cnt,fa[NN],dfn[NN],siz[NN],son[NN],top[NN],dep[NN];
    void dfs1(int s,int f){
        siz[s]=1; dep[s]=dep[f]+1; fa[s]=f;
        for(int v:e[s]) if(v!=f){
            dfs1(v,s);
            siz[s]+=siz[v];
            if(siz[v]>siz[son[s]]) son[s]=v;
        }
    }
    void dfs2(int s,int t){
        dfn[s]=++cnt; top[s]=t;
        if(!son[s]) return;
        dfs2(son[s],t);
        for(int v:e[s])
            if(v!=son[s]&&v!=fa[s]) dfs2(v,v);
    }
    int LCA(int x,int y){
        while(top[x]^top[y])
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]];
        return dep[x]<dep[y]?x:y;
    }
    void UPD(int x,int y,int f,int d){
        int lca=LCA(x,y),tmpx=0,tmpy=dep[x]+dep[y]-(dep[lca]<<1)+2;
        while(top[x]^top[lca]){
            tmpx+=dep[x]-dep[top[x]]+1;
            update(root[now],dfn[top[x]],dfn[x],f+(tmpx-1)*d,-d);
            x=fa[top[x]];
        }
        while(top[y]^top[lca]){
            tmpy-=dep[y]-dep[top[y]]+1;
            update(root[now],dfn[top[y]],dfn[y],f+(tmpy-1)*d,d);
            y=fa[top[y]];
        }
        ++tmpx; --tmpy;
        if(dep[x]<dep[y]) update(root[now],dfn[x],dfn[y],f+(tmpx-1)*d,d);
        else update(root[now],dfn[y],dfn[x],f+(tmpy-1)*d,-d);
    }
    LL ASK(int x,int y){
        LL res=0;
        while(top[x]^top[y]){
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            res+=query(root[now],dfn[top[x]],dfn[x]);
            x=fa[top[x]];
        }
        if(dep[x]>dep[y]) swap(x,y);
        return res+query(root[now],dfn[x],dfn[y]);
    }
} using namespace Tree_Chain;

signed main(){
    n=read(); m=read();
    for(int a,b,i=1;i<n;i++){
        a=read(); b=read();
        e[a].push_back(b);
        e[b].push_back(a);
    }
    dfs1(1,0); dfs2(1,1);
    while(m--){
        cin>>op;
        if(op=='c'){
            int x=read()^ans,y=read()^ans,f=read(),d=read();
            root[++mem]=root[now]; now=mem;
            UPD(x,y,f,d);
        } else if(op=='q'){
            int x=read()^ans,y=read()^ans;
            write(ans=ASK(x,y),'\n');
        } else now=read()^ans;
    }
    return 0;
}

[BZOJ1901] Dynamic Rankings

放个树状数组套主席树板子。

Code
#include<bits/stdc++.h>
using namespace std;

namespace IO{
    typedef long long LL;
    typedef double DB;
    int read(){
        int x=0,f=0; char ch=getchar();
        while(ch<'0'||ch>'9'){ f|=(ch=='-'); ch=getchar(); }
        while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return f?-x:x;
    } char output[50];
    void write(LL x,char sp){
        int len=0;
        if(x<0) x=-x, putchar('-');
        do{ output[len++]=x%10+'0'; x/=10; }while(x);
        for(int i=len-1;~i;i--) putchar(output[i]); putchar(sp);
    }
    void ckmin(int &x,int y){ x=x<y?x:y; }
    void ckmax(int &x,int y){ x=x>y?x:y; }
} using namespace IO;

const int NN=100010;
int d,n,m,ext,a[NN],has[NN];
struct opt{ int x,y,k; char op; }q[NN];

namespace PersSegmentTree_in_BIT{
    const int TN=NN<<6;
    int tot,cntl,cntr,root[TN],lc[TN],rc[TN],sum[TN],nowr[NN],nowl[NN];
    void insert(int& rt,int pos,int val,int l=1,int r=ext){
        if(!rt) rt=++tot; sum[rt]+=val;
        if(l==r) return;
        int mid=l+r>>1;
        if(pos<=mid) insert(lc[rt],pos,val,l,mid);
        else insert(rc[rt],pos,val,mid+1,r);
    }
    void update(int loc,int pos,int val){
        while(loc<=n){
            insert(root[loc],pos,val);
            loc+=loc&(-loc);
        }
    }
    void getpos(int loc,int *pos,int& cnt){
        while(loc){
            pos[++cnt]=root[loc];
            loc-=loc&(-loc);
        }
    }
    int query(int k,int l=1,int r=ext){
        if(l==r) return l;
        int mid=l+r>>1,siz=0;
        for(int i=1;i<=cntr;i++) siz+=sum[lc[nowr[i]]];
        for(int i=1;i<=cntl;i++) siz-=sum[lc[nowl[i]]];
        if(siz>=k){
            for(int i=1;i<=cntr;i++) nowr[i]=lc[nowr[i]];
            for(int i=1;i<=cntl;i++) nowl[i]=lc[nowl[i]];
            return query(k,l,mid);
        }else{
            for(int i=1;i<=cntr;i++) nowr[i]=rc[nowr[i]];
            for(int i=1;i<=cntl;i++) nowl[i]=rc[nowl[i]];
            return query(k-siz,mid+1,r);
        }
    }
} using namespace PersSegmentTree_in_BIT;

signed main(){
    n=read(); m=read();
    for(int i=1;i<=n;i++) has[++ext]=a[i]=read();
    for(int i=1;i<=m;i++){
        cin>>q[i].op; q[i].x=read(); q[i].y=read();
        if(q[i].op=='C') has[++ext]=q[i].y;
        else q[i].k=read();
    }
    sort(has+1,has+ext+1); ext=unique(has+1,has+ext+1)-has-1;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(has+1,has+ext+1,a[i])-has;
        update(i,a[i],1);
    }
    for(int i=1;i<=m;i++) if(q[i].op=='C')
        q[i].y=lower_bound(has+1,has+ext+1,q[i].y)-has;
    for(int i=1;i<=m;i++)
        if(q[i].op=='C'){
            update(q[i].x,a[q[i].x],-1);
            update(q[i].x,q[i].y,1);
            a[q[i].x]=q[i].y;
        } else{
            getpos(q[i].x-1,nowl,cntl=0);
            getpos(q[i].y,nowr,cntr=0);
            write(has[query(q[i].k)],'\n');
        }
    return 0;
}
posted @ 2021-12-16 21:07  keen_z  阅读(59)  评论(3编辑  收藏  举报