ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

9月17日数据结构专题考试题解(待更新)

2019-09-17 16:01:05  阅读:270  来源: 互联网

标签:ch 17 int res 考试题 数据结构 rg 1005 include


9月17日数据结构专题考试题解

$ By~wch $



第一题:加法(线段树)

题目描述:

给一个 $ n $ 阶排列 $ b $ ,要求维护一个初值全为 $ 0 $ 的数组 $ a_i $ ,支持 $ q $ 次如下操作:

  1. 给出 $ l,r $ ,将 $ a_l,a_{l+1}...a_{r-1},a_{r} $ 全部加 \(1\) 。
  2. 给出 $ l,r $ ,查询 $ \sum_{i=l}^{r} \lfloor \frac{a_i}{b_i} \rfloor $

数据范围: $ n\leq 3\times 10^5$


$ solution: $

很让人头疼的一道题目,当时觉得第一个操作肯定是一个突破口,因为每次只加 $ 1 $ 。这是一个很好的性质,在结合一下题目的条件( $ b $ 是个排列!),于是就算了一下最坏情况(不断区间加)的 $ \sum a_i =4\times 10^6 $ ,线段树每一次修改复杂度为 $ logn $ ,两者一乘复杂度可以接受。于是边只要看能不能实现这棵线段树了。

用线段树记录区间总和,用懒标记记录修改,然后每次加入时判断是否有元素达到了 $ b_i $ 权值需要加一,如果有就 $ pushdown $ 操作往下传,一直到那个要修改的叶子节点。(实现过程有点复杂,大家可以自己想,但是我自己都觉得常数可能过不了)


$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int
#define zuo k<<1,l,mid
#define you k<<1|1,mid+1,r
#define midd int mid=(l+r)>>1

using namespace std;

int n,q,sx,sy,sv;
int mi[300005<<2];
int da[300005<<2];
int lz[300005<<2];
bool yz[300005<<2];

inline ll qr(){
    register char ch; bool sign=0; register ll res=0;
    while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    if(sign)return -res; else return res;
}

inline void build(int k,int l,int r){
    if(l==r){yz[k]=1; mi[k]=qr(); return ;}
    midd; build(zuo); build(you);
    mi[k]=min(mi[k<<1],mi[k<<1|1]);
}

inline void push(int k){
    if(yz[k]){ //到达叶子节点,直接改
        da[k]+=lz[k]/mi[k]; //加多少
        lz[k]%=mi[k]; //还剩多少
        return ;
    }
    rg l=k<<1,r=k<<1|1;
    lz[l]+=lz[k]; lz[r]+=lz[k];
    if(lz[l]>=mi[l]) push(l); //一直到需要更改的叶子节点
    if(lz[r]>=mi[r]) push(r);
    mi[k]=min(mi[l]-lz[l],mi[r]-lz[r]); //维护还需要多少次加操作达到上界
    da[k]=da[l]+da[r]; lz[k]=0; //记得清零
}

inline void add(int k,int l,int r){
    if(sx<=l&&r<=sy) ++lz[k];
    else { midd; if(lz[k])push(k); //标记下放
        if(sx<=mid) add(zuo);
        if(sy>mid) add(you);
    }
    if(l!=r)mi[k]=min(mi[k<<1]-lz[k<<1],mi[k<<1|1]-lz[k<<1|1]); 
    if(lz[k]>=mi[k]) push(k); //mi数组:最少还需要多少次加操作,就会有元素达到上界
    if(l!=r)da[k]=da[k<<1]+da[k<<1|1]; //区间和
}

inline void ask(int k,int l,int r){
    if(sx<=l&&r<=sy){sv+=da[k]; return ;}
    midd;
    if(sx<=mid) ask(zuo);
    if(sy>mid) ask(you);
}

int main(){
    n=qr(); q=qr(); build(1,1,n);
    for(rg i=1;i<=q;++i){
        rg op=qr(); sx=qr(),sy=qr();
        if(op==1) add(1,1,n);
        else sv=0,ask(1,1,n);
        if(op==2)printf("%d\n",sv);
    }
    return 0;
}


第二题:区间(并查集+计数)

题目描述:

给出一个长度为 $ n $ 的序列 $ a_i $ ,选出两个不相交的区间 $ [ l_1,r_1 ] $ 和 $ [l_2,r_2] $ ,获得的收益是两个区间的区间最小值的最小值。求所有不同的选法的收益之和模 \(10^9+7\) 。

数据范围:$ n\leq 10^6 , a_i \leq 10^9 $


$ solution: $


$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int

using namespace std;

const int mod=1e9+7;

int n,tot,ans;
int s[1000005];
int t[1000005];
int r[1000005];
int g[1000005];
int f[1000005];

struct su{
    int v,id;
    inline bool operator <(const su &i)const{
        return v>i.v;
    }
}a[1000005];

inline int qr(){
    register char ch; register bool sign=0; rg res=0;
    while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    if(sign)return -res; else return res;
}

inline int get(const int &x){
    if(x==s[x])return x;
    return s[x]=get(s[x]); //路径压缩
}

inline void bin(int x,int y){
    x=get(x); y=get(y);
    if(t[x]>t[y])swap(x,y); //按秩合并
    tot-=g[t[x]]+g[t[y]];
    t[y]+=t[x]; s[x]=y; r[y]=max(r[x],r[y]);
    tot+=g[t[y]];
    while(tot<0) tot+=mod;
    while(tot>=mod) tot-=mod;
}

int main(){
    n=qr();
    for(rg i=1;i<=n;++i) g[i]=(g[i-1]+i)%mod;
    for(rg i=1;i<=n;++i) f[i]=(g[i]+f[i-1])%mod;
    for(rg i=1;i<=n;++i) a[i]=su{qr(),i};
    sort(a+1,a+n+1);
    for(rg i=1;i<=n;++i){
        rg k=a[i].id; ++tot;
        s[k]=k; t[k]=1; r[k]=k;
        if(s[k-1]) bin(k-1,k);
        if(s[k+1]) bin(k,k+1);
        rg j=get(k),y=r[j]-k,x=t[j]-y-1,v=(tot-g[t[j]]+mod)%mod,tt=(ll)(x+1)*(y+1)%mod;
        // 区间编号 右边长度 左边长度    其他区间可组成的片段个数 这个区间里包含k的片段数       
        (ans+=(ll)a[i].v*(((ll)(x+1)*f[y]+(ll)(y+1)*f[x]+(ll)tt*v)%mod)%mod)%=mod;
        //        权值     左边片段贡献    右边片段贡献   其他区间贡献
    }
    printf("%d\n",ans);
    return 0;
}


第三题:序列(可持久化trie+特殊性质)


$ solution: $

加班中…….


$ code: $



第四题:玉蟾宫(悬线法)

题目链接:洛谷 P4147 玉蟾宫


$ solution: $

已经写过题解了,在写悬线法详解的时候讲的。

挂个链接:最大子矩阵的两种求法


$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int

using namespace std;

int n,m,ans;
int a[1005][1005];
int l[1005][1005];
int r[1005][1005];
int h[1005][1005];

inline ll qr(){
    register char ch; bool sign=0; register ll res=0;
    while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    if(sign)return -res; else return res;
}

int main(){
    n=qr(); m=qr(); char ch;
    for(rg i=1;i<=n;++i){
        for(rg j=1;j<=m;++j){
            while((ch=getchar())!='F'&&ch!='R');
            a[i][j]=(ch=='F');
            if(a[i][j]){
                l[i][j]=l[i][j-1]+1; //向左延伸
                h[i][j]=h[i-1][j]+1; //向上延伸
            }
        }
    }
    for(rg i=1;i<=n;++i)
        for(rg j=m;j>=1;--j)
            if(a[i][j])r[i][j]=r[i][j+1]+1; //向右延伸
    for(rg i=1;i<=n;++i){
        for(rg j=m;j>=1;--j){
            if(a[i-1][j])continue;
            rg x=1e9,y=1e9;
            for(rg k=i;a[k][j]&&k<=n;++k){
                if(l[k][j]<x)x=l[k][j];
                if(r[k][j]<y)y=r[k][j];
                ans=max(ans,(x+y-1)*(k-i+1));
            }
        }
    }
    printf("%d\n",ans*3);
    return 0;
}


第五题:Eden 的新背包问题

题目链接:洛谷 P4095 [HEOI2013] Eden 的新背包问题


$ solution: $


$ code: $

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define rg register int

using namespace std;

int n,m,q;
int a[1005];
int b[1005];
int c[1005];
int s[1005][10005];
int k[1005][10005];
int qv[10005];
int qt[10005];

inline int qr(){
    register char ch; register bool sign=0; rg res=0;
    while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
    while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
    if(sign)return -res; else return res;
}

inline void dp(int f[],int w,int v,int t){ //讲一个物品加入背包
    for(rg i=0;i<w;++i){ 
        rg l=1,r=0; qv[++r]=f[i]; qt[r]=0; //单调队列优化
        for(rg j=i+w;j<=m;j+=w){
            rg y=j/w,x=f[j]-y*v;
            while(l<=r&&y-qt[l]>t)++l;
            f[j]=max(f[j],y*v+qv[l]); //蓝书上有讲
            while(l<=r&&qv[r]<=x)--r;
            qv[++r]=x; qt[r]=y;
        }
    }
}

int main(){
    n=qr();  m=1e4;
    for(rg i=1;i<=n;++i)
        a[i]=qr(),b[i]=qr(),c[i]=qr();
    q=qr();
    for(rg i=1;i<=n;++i){
        for(rg j=0;j<=m;++j)
            s[i][j]=s[i-1][j]; //记录每一步过程
        dp(s[i],a[i],b[i],c[i]); //做一遍前缀
    }
    for(rg i=n;i>=1;--i){
        for(rg j=0;j<=m;++j)
            k[i][j]=k[i+1][j]; //记录每一步过程
        dp(k[i],a[i],b[i],c[i]); //做一遍后缀
    }
    for(rg i=1;i<=q;++i){
        rg x=qr(),y=x+2,v=qr(),ans=0;
        for(rg j=0;j<=v;++j) //找到前后两个位置,合并
            ans=max(ans,s[x][j]+k[y][v-j]);
        printf("%d\n",ans);
    }
    return 0;
}

标签:ch,17,int,res,考试题,数据结构,rg,1005,include
来源: https://www.cnblogs.com/812-xiao-wen/p/11534516.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有