9.27数据结构练习赛

这里写图片描述# 欢迎使用Markdown编辑器写博客
题解:莫队算法模板题!
当然也可以用线段树解决:
离线+ 线段树
将询问R 值排序,考虑维护一个数组A,表示当前询问R 值确定,对于每
一个L 值答案是多少
假设一直R-1 时的数组A’,我们需要求R 的数组A
预处理处每个位置相同值的上一次出现位置prv[i]。
那么对于A 数组中所有prv[r]+1 到r 的位置,都要加v[r],因为这些位置
到r 的区间v[r] 只出现过1 次。
同理prv[prv[r]]+1 到prv[r] 需要-v[r]
同理prv[prv[prv[r]]]+1 到prv[prv[r]] 需要+3*v[r]
同理1 到prv[prv[prv[r]]] 需要+v[r]
区间修改单点查询,线段树或树状数组都可以。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
const int MAXN=1e5+4;
int n,m,siz;
int cnt[MAXN],num[MAXN],bel[MAXN];
ll ans[MAXN],ret=0;
struct Q {
    int l,r,id;
    friend bool operator <(const Q &x,const Q &y) {
        return bel[x.l]==bel[y.l]?x.r<y.r:x.l<y.l;
    }
}q[MAXN];
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
int main() {
    freopen("abnormal.in","r",stdin);
    freopen("abnormal.out","w",stdout);
    n=read(),m=read(),siz=(int)sqrt((double)n);
    for (register int i=1;i<=n;++i) num[i]=read(),bel[i]=i/siz;
    for (register int i=1;i<=m;++i) q[i].l=read(),q[i].r=read(),q[i].id=i;
    sort(q+1,q+m+1);
    for (register int i=1,l=1,r=0;i<=m;++i) {
        while (l>q[i].l) {
            ++cnt[num[--l]];
            ret+=num[l];
            if (cnt[num[l]]==3) ret+=(num[l]<<1);
            if (cnt[num[l]]==2) ret-=(num[l]<<1);
        }
        while (r<q[i].r) {
            ++cnt[num[++r]];
            ret+=num[r];
            if (cnt[num[r]]==3) ret+=(num[r]<<1);
            if (cnt[num[r]]==2) ret-=(num[r]<<1);
        }
        while (l<q[i].l) {
            --cnt[num[l++]];
            ret-=num[l-1];
            if (cnt[num[l-1]]==1) ret+=(num[l-1]<<1);
            if (cnt[num[l-1]]==2) ret-=(num[l-1]<<1);
        }
        while (r>q[i].r) {
            --cnt[num[r--]];
            ret-=num[r+1];
            if (cnt[num[r+1]]==1) ret+=(num[r+1]<<1);
            if (cnt[num[r+1]]==2) ret-=(num[r+1]<<1);
        }
        ans[q[i].id]=ret;
    }
    for (register int i=1;i<=m;++i) printf("%I64d\n",ans[i]);
    return 0;
}

这里写图片描述
题解:trie+ 启发式合并
首先,预处理处每个节点到根节点路径异或和v[x]
则x,y 路径异或和为v[x] xor v[y] xor a[lca(x,y)]
其次,我们可以通过trie 树贪心来O(log) 求出一个集合和一个数的异或最
大值。
最后,集合的合并如果每次都将小的合并到大的上面去,复杂度O(nlogn),
名叫启发式合并
于是这道题我们就可以用trie 维护子树v 值的集合,并通过启发式合并向
上进行。每次贪心找最大路径异或和

题解2:树链剖分+可持久化Trie
大概就是用双指针扫当前子树对应的dfs序区间,每次处理后r就往后移,而起点l不动,相当于就是将处理过的启发式合并!由于时间复杂度限制,需要树链剖分来优化对重儿子的处理。由于这样的优化只能用一次,所以肯定选size最大的儿子即重儿子来优化。为什么只能用一次呢,因为只有第一次Trie_query是第1个儿子区间与当前根的询问,后面就是第i个儿子与(根+第1个儿子区间+第2个儿子区间+…+第i-1个儿子区间)询问。
我估计我说了还是没几个人看得懂,所以附上大佬的博客地址orz
戳这里看大佬的原创解法

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<utility>
using namespace std;
#define pii pair<int,int>
#define mp(x,y) make_pair(x,y)
const int MAXN=1e5+4;
int n,head[MAXN],edge=0;
struct EDGE {
    int v,nxt;
}e[MAXN<<1];
int siz[MAXN],in[MAXN],out[MAXN],rk[MAXN],son[MAXN],fa[MAXN],dis[MAXN],tim=0;
int a[MAXN],seq[MAXN];
inline int read() {
    int x=0;char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
inline void adde(int u,int v) {
    e[edge].nxt=head[u],e[edge].v=v,head[u]=edge++;
    e[edge].nxt=head[v],e[edge].v=u,head[v]=edge++;
}
/*----------DCP----------*/
void dfs1(int p,int f) {
    siz[p]=1,fa[p]=f;
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (v^f) {
            dis[v]=dis[p]^a[v];
            dfs1(v,p);
            siz[p]+=siz[v];
            if (son[p]==-1||siz[son[p]]<siz[v]) son[p]=v;
        }
    }
}
void dfs2(int p) {
    in[p]=++tim,rk[in[p]]=p;
    seq[tim]=dis[p];
    if (son[p]==-1) {out[p]=tim;return ;}
    dfs2(son[p]);//保证先dfs重儿子
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (v^fa[p]&&v^son[p])
            dfs2(v); 
    }
    out[p]=tim;
}
/*----------Persistent Trie----------*/
struct TRIE {
    int son[2],w;
    TRIE() {w=0;}
}t[MAXN*33];
int tot=0,root[MAXN];
void Insert(int pre,int &rt,int d,int step) {
    t[rt=++tot]=t[pre];
    ++t[rt].w;
    if (step<0) return ;
    int p=(d>>step)&1; 
    Insert(t[pre].son[p],t[rt].son[p],d,step-1);
}
int query(int d,int pre,int rt,int step) {
    if (step<0) return 0;
    int p=(d>>step)&1;
    if (t[t[rt].son[!p]].w-t[t[pre].son[!p]].w)
        return (1<<step)+query(d,t[pre].son[!p],t[rt].son[!p],step-1);
    else return query(d,t[pre].son[p],t[rt].son[p],step-1);
}
int main() {
    freopen("irregular.in","r",stdin);
    freopen("irregular.out","w",stdout);
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
    n=read();
    for (register int i=1;i<=n;++i) a[i]=read();
    for (register int i=1;i<n;++i) {
        int u=read(),v=read();
        adde(u,v);
    }
    dis[1]=a[1];
    dfs1(1,0);
    dfs2(1);
//  for (int i=1;i<=n;++i) printf("%d %d %d %d\n",in[i],out[i],dis[i],seq[i]);
    for (register int i=1;i<=tim;++i) Insert(root[i-1],root[i],seq[i],31);
    int l,r,v;
    for (register int i=1;i<=n;++i) {
        int ans=a[i];
        if (fa[rk[in[i]+1]]==i) {
            v=son[rk[in[i]]];//=rk[in[i]+1]
            l=in[v],r=out[v];
            ans=max(ans,query(seq[in[i]]^a[i],root[l-1],root[r],31));
            l=in[i];
        }
        else {
            printf("%d ",ans);
            continue;
        }
        while (fa[rk[out[v]+1]]==i) {
            v=rk[out[v]+1];
            for (int p=in[v];p<=out[v];++p)
                ans=max(ans,query(seq[p]^a[i],root[l-1],root[r],31));
            r=out[v];
        }
        printf("%d ",ans);
    }
    return 0;
}

这里写图片描述
题解:双堆维护动态中位数,本来大部分平衡树是可以实现的但是被卡了,于是有些大佬就写了RBT,orzorzorz。

双堆实现:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int MAXN=1e6+4,MOD=1e9+7;
int a,b,c,n,f;
ll ret=1;
priority_queue<int, vector<int>, less<int> > qmax;//bigger root
priority_queue<int, vector<int>, greater<int> > qmin;//smaller root
inline void add(int x) {
    if (qmin.empty()) {qmin.push(x);return ;}
    if (x>qmin.top()) qmin.push(x);
    else qmax.push(x);
    if (qmin.size()<qmax.size()) {//while
        qmin.push(qmax.top());
        qmax.pop();
    }
    if (qmin.size()>qmax.size()+1) {//while
        qmax.push(qmin.top());
        qmin.pop();
    }
}
int main() {
    freopen("unnormal.in","r",stdin);
    freopen("unnormal.out","w",stdout);
    scanf("%d%d%d%d",&a,&b,&c,&n);
    add(1);
    for (register int i=2;i<=n;++i) {
        add(f);
        f=(1ll*a*qmin.top()+1ll*b*i+c)%MOD;
        ret+=f;
    }
    cout<<ret<<endl;
    return 0;
}

RBT实现:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=1e6+4,MOD=1e9+7;
int a,b,c,n;
int f[MAXN];
ll ret=1;
struct RBt{
    int data,s,c;
    bool col;
    RBt *fa,*ch[2];
    inline void set(int _v,bool _col,int i,RBt *p){
        data=_v,col=_col,s=c=i;
        fa=ch[0]=ch[1]=p;
    }
    inline void push_up(){s=ch[0]->s+ch[1]->s+c;}
    inline void push_down(){for(RBt *x=this; x->s; x=x->fa)x->s--;}
    inline int cmp(int v)const{return data==v?-1:v>data;}
};
struct RedBlackTree{
    int top;
    RBt *root,*null;
    RBt stack[MAXN],*tail,*se[MAXN];
    void init(){
        tail=&stack[0],null=tail++;
        null->set(0,0,0,NULL);
        root=null,top=0;
    }
    inline RBt *newRBt(int v){
        RBt *p=null;
        if(!top)p=tail++;else p=se[--top];
        p->set(v,1,1,null);
        return p;
    }
    inline void rotate(RBt* &x,bool d ){
        RBt *y=x->ch[!d];
        x->ch[!d]=y->ch[d];
        if(y->ch[d]->s)y->ch[d]->fa=x;y->fa=x->fa;
        if(!x->fa->s)root=y;else x->fa->ch[x->fa->ch[0]!=x]=y;
        y->ch[d]=x,x->fa=y,y->s=x->s,x->push_up();
    }
    inline void insert(int v){
        RBt *x=root,*y=null;
        while(x->s){
            x->s++,y=x;
            int d=x->cmp(v);
            if(-1==d){x->c++;return;}
            x=x->ch[d];
        }
        x=newRBt(v);
        if(y->s)y->ch[v>y->data]=x;else root=x;
        x->fa=y;insert_fix(x);
    }
    inline void insert_fix(RBt* &x){
        while(x->fa->col){
            RBt *par=x->fa,*Gp=par->fa;
            bool d=par==Gp->ch[0];
            RBt *uncle=Gp->ch[d];
            if(uncle->col)par->col=uncle->col=0,Gp->col=1,x=Gp;
            else if(x==par->ch[d])rotate(x=par,!d);
            else Gp->col=1,par->col=0,rotate(Gp,d);
        }
        root->col=0;
    }
    inline RBt *find(RBt *x,int data){
        while(x->s&&x->data != data)x=x->ch[x->data < data];
        return x;
    }
    inline int kth(int k){
        int t;
        RBt *x=root;
        for(; x->s;){
            t=x->ch[0]->s;
            if(k<=t)x=x->ch[0];
            else if(t+1<=k&&k<=t+x->c)break;
            else k-=t+x->c,x=x->ch[1];
        }
        return x->data;
    }
}rbt;

int main() {
    freopen("unnormal.in","r",stdin);
    freopen("unnormal.out","w",stdout);
    scanf("%d%d%d%d",&a,&b,&c,&n);
    rbt.init();
    rbt.insert(f[1]=1);
    for (register int i=2;i<=n;++i) {
        int temp=i>>1;
        int M=rbt.kth(temp);
        f[i]=(1ll*a*M%MOD+1ll*b*i%MOD+c)%MOD;
        rbt.insert(f[i]);
        ret+=f[i];
    }
    cout<<ret<<endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值