zoukankan      html  css  js  c++  java
  • [CZYZ2016]day7

    数列

    Description

    给定一个序列,初始为空。依次将\(1-n\)插入序列,其中\(i\)插到当前第\(a_i\)个数的右边(\(a_i=0\)表示插到序列最左边)。求最终序列。

    Input

    第一行一个整数\(n\)。第二行\(n\)个正整数\(a_1-a_n\)

    Output

    输出一行\(n\)个整数表示最终序列,数与数之间用一个空格隔开。

    Sample Input

    5 
    0 1 1 0 3
    

    Sample Output

    4 1 3 5 2
    

    HINT

    \(n<=10^6,0\;\leq\;a_i<i\).

    Solution

    首先,看到这种题显然倒着处理比较容易。

    其次,这题\(O(nlogn)\)能过(可是我的机子跑不过啊!!!)

    所以可以用线段树维护当前区间剩余可填格子的个数,每次寻找第\(a[i]+1\)个空格在哪。

    #include<cmath>
    #include<ctime>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 5000005
    using namespace std;
    struct linetree{
        int l,r,s,b;
    }lt[N];
    int a[N],s[N],ans[N],n;
    inline int read(){
        int ret=0;char c=getchar();
        while(!isdigit(c))
            c=getchar();
        while(isdigit(c)){
            ret=ret*10+c-'0';
            c=getchar();
        }
        return ret;
    }
    inline void build(int u,int l,int r){
        lt[u].l=l;lt[u].r=r;lt[u].s=r-l+1;
        if(l==r) return;
        int lef=u<<1,rig;rig=lef|1;
        int mid=(l+r)>>1;
        build(lef,l,mid);build(rig,mid+1,r);
    }
    inline int ask(int u,int k){
        if(lt[u].l==lt[u].r)
            return lt[u].l;
        int l=u<<1,r;r=l|1;
        if(lt[u].s==k)
            if(lt[l].s==k) ask(l,k);
            else ask(r,k-lt[l].s);
        else if(lt[l].s>=k) ask(l,k);
        else ask(r,k-lt[l].s);
        
    }
    inline bool chk(linetree l,int x){
        return l.l<=x&&l.r>=x;
    }
    inline void dec(int u,int x){
        if(chk(lt[u],x)) lt[u].s--;
        if(lt[u].l<lt[u].r){
            int l=u<<1,r;r=l|1;
            if(chk(lt[l],x)) dec(l,x);
            else if(chk(lt[r],x)) dec(r,x);
        }
        else lt[u].b=true;
    }
    inline void init(){
        n=read();build(1,1,n);
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=n,k;i;i--){
            k=ask(1,a[i]+1);
            ans[k]=i;dec(1,k);
        }
        for(int i=1;i<=n;i++)
            printf("%d ",ans[i]);
        printf("\n");
    }
    int main(){
        freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        init();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    

    背包问题

    Description

    \(n\)个物品和一个大小为\(m\)的背包,每个物品有大小和价值,求背包里最多能放下多少价值的物品。

    Input

    第一行两个整数\(n,m\)。接下来\(n\)行每行两个整数\(x_i,w_i\),表示第\(i\)个物品的大小和价值。

    Output

    一行一个整数表示最大价值。

    Sample Input

    5 100 
    95 80 
    4 18 
    3 11 
    99 100 
    2 10
    

    Sample Output

    101
    

    HINT

    \(n\;\leq\;40,0\;\leq\;m\;\leq\;10^{18},0\;\leq\;x_i,w_i\;\leq\;10^{15}\).

    Solution

    显然数据不适合\(dp\),由于\(n\)比较小,所以会想到折半搜索。

    把前\(n/2\)个物品的所有方案存下来,删去明显劣的。

    再枚举剩余的物品,对于每种方案,二分求与前\(n/2\)个物品能组成的最大价值。

    (我以前有做过类似的题,只不过求的是是否存在总价值为\(w\)的方案,然而这题当时我还没想出来\(QAQ\)

    #include<cmath>
    #include<ctime>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 45
    #define M 2000000
    using namespace std;
    typedef long long ll;
    struct thing{
        ll x,w;
    }a[N],b[M];
    int n,nn,cnt=-1,tot=1;
    ll m,ans;
    inline ll search(ll m){
        if(m<b[1].x) return 0; 
        int l=1,r=tot,mid;
        while(l<r){
            mid=l+r+1>>1;
            if(b[mid].x<=m) l=mid;
            else r=mid-1;
        }
        return b[l].w;
    }
    inline void dfs1(int u,ll sw,ll sx){
        if(u>nn){
            b[++cnt]=(thing){sx,sw};
            return;
        }
        dfs1(u+1,sw,sx);
        if(sx+a[u].x<=m)
            dfs1(u+1,sw+a[u].w,sx+a[u].x);
    }
    inline void dfs2(int u,ll sw,ll sx){
        if(u>n){
            sw+=search(m-sx);
            ans=max(ans,sw);
            return;
        }
        dfs2(u+1,sw,sx);
        if(sx+a[u].x<=m)
            dfs2(u+1,sw+a[u].w,sx+a[u].x);
    }
    inline bool cmp(thing x,thing y){
        if(x.x!=y.x) return x.x<y.x;
        return x.w>y.w;
    }
    inline void init(){
        scanf("%d%lld",&n,&m);
        for(int i=1;i<=n;++i)
            scanf("%lld%lld",&a[i].x,&a[i].w);
        nn=n>>1;dfs1(1,0,0);
        sort(b+1,b+1+cnt,cmp);
        for(int i=2;i<=cnt;i++)
            if(b[i].x>b[i-1].x) b[++tot]=b[i];
        for(int i=2;i<=tot;i++)
            b[i].w=max(b[i].w,b[i-1].w);
        dfs2(nn+1,0,0);
        printf("%lld\n",ans);
    }
    int main(){
        freopen("pack.in","r",stdin);
        freopen("pack.out","w",stdout);
        init();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
    

    线段树

    Description

    有一棵线段树,根为\([1,n]\),有所不同的是,每个节点\([l,r]\)的孩子为\([l,k]\),\([k+1,r]\),其中\(l\;\leq\;k<r\)\(k\)的值由你决定。给定\(m\)个区间,定义区间\([a_i,b_i]\)会访问到节点\([l,r]\)仅当他们会相交。求每次访问的结点个数之和的最小值。

    Input

    第一行两个整数\(n,m\),接下来\(m\)行每行两个整数\(a_i,b_i\)

    Output

    一行一个整数表示答案。

    Sample Input

    6 6 
    1 4 
    2 6 
    3 4 
    3 5 
    2 3 
    5 6
    

    Sample Output

    40
    

    HINT

    \(n\;\leq\;5000,m\;\leq\;10^5\)

    Solution

    \(f[i][j]\)表示节点\([i,j]\)的子树中被访问的最少次数,\(s[i][j]\)表示与节点\([i,j]\)相交的区间数。

    \(f[i][j]=min\{f[i][k]+f[k+1][j]\}+s[i][j]\;(i\;\leq\;k<j)\)

    直接枚举是\(O(n^3)\),显然会\(T\)

    用四边形不等式优化能压到\(O(n^2)\).

    证明:

    ①当\(a\;\leq\;b<c\;\leq\;d\)时,易证\(s[a][c]+s[b][d]=s[b][c]+s[a][d],s[\;][\;]\)满足四边形不等式。

    ②当\([i,j]\)属于\([i',j']\)时,\(s[i,j]\;\leq\;s[i',j'],s[\;][\;]\)满足区间包含关系单调。

    因为同时满足①②,所以\(f[\;][\;]\)满足四边形不等式。

    记使\(f[i][j]\)取最小值时的\(k\)\(g[i][j]\),则\(g[i][j-1]\;\leq\;g[i][j]\;\leq\;g[i+1][j]\)

    易证枚举\(k\)的总时间复杂度为\(O(n)\)

    #include<cmath>
    #include<ctime>
    #include<stack>
    #include<queue>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define N 5005
    #define M 10005
    #define INF 500000005
    using namespace std;
    int f[N][N],g[N][N],s[N][N],n,m;
    inline int read(){
        int ret=0;char c=getchar();
        while(!isdigit(c))
            c=getchar();
        while(isdigit(c)){
            ret=ret*10+c-'0';
            c=getchar();
        }
        return ret;
    }
    inline void init(){
        n=read();m=read();
        for(int i=1,j,k;i<=m;++i){
            j=read();k=read();
            ++s[k][j];
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=n;++j)
                s[i][j]+=s[i][j-1];
        for(int i=n;i;--i)
            for(int j=1;j<=n;++j)
                s[i][j]+=s[i+1][j];
        for(int i=1;i<=n;++i){
            f[i][i]=s[i][i];
            g[i][i]=i;
        }
        for(int l=1;l<n;++l){
            for(int i=1,j;i+l<=n;++i){
                j=i+l;f[i][j]=INF;
                for(int k=g[i][j-1];k<=g[i+1][j];++k){
                    if(f[i][k]+f[k+1][j]<f[i][j]){
                        f[i][j]=f[i][k]+f[k+1][j];
                        g[i][j]=k;
                    }
                }
                f[i][j]+=s[i][j];
            }
        }
        printf("%d\n",f[1][n]);
    }
    int main(){
        freopen("segment.in","r",stdin);
        freopen("segment.out","w",stdout);
        init();
        fclose(stdin);
        fclose(stdout);
        return 0;
    }
  • 相关阅读:
    PAT 甲级 1132 Cut Integer (20 分)
    AcWing 7.混合背包问题
    AcWing 9. 分组背包问题
    AcWing 5. 多重背包问题 II
    AcWing 3. 完全背包问题
    AcWing 4. 多重背包问题
    AcWing 2. 01背包问题
    AcWing 875. 快速幂
    AcWing 874. 筛法求欧拉函数
    AcWing 873. 欧拉函数
  • 原文地址:https://www.cnblogs.com/AireenYe/p/CZYZ2016day7.html
Copyright © 2011-2022 走看看