• [APIO2009]


    来自FallDream的博客,不经允许,请勿转载,谢谢。

    ------------------------------------------------------

    1.Oil

    给定一个n*m的矩阵,你要从中选出恰好3个k*k的不想交的矩阵,并使得矩阵数字的和最大。n,m<=1500

    题解:很显然,三个矩阵只有两种排布方案:1)先横着或者竖着割成两段,然后在其中一边再分成两段。3)横着或者竖着分成三段。

    所以我们用A[i][j]表示前i行j列选一个的最大值,其他三个角同理,然后处理一下横着和竖着的一个区间的最大值就可以啦。

    #include<iostream>
    #include<cstdio>
    #define MN 1505
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
     
    int n,m,k,ans=0;
    int f[4][MN][MN];
    int g[MN][MN],h[MN],e[MN][MN],d[MN][MN],c[MN];
    int s[MN][MN];
     
    inline int calc(int x,int y){return (x<=n&&y<=m&&x>=k&&y>=k)?(s[x][y]-s[x-k][y]-s[x][y-k]+s[x-k][y-k]):0;}
     
    int main()
    {
        //freopen("test0.in","r",stdin);
        n=read();m=read();k=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                scanf("%d",&s[i][j]);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];
        for(int i=k;i<=n;i++)
            for(int j=k;j<=m;j++)
                g[i][j]=calc(i,j),h[i]=max(h[i],g[i][j]),c[j]=max(c[j],g[i][j]);
        for(int i=k;i<=n;i++)
            for(int j=k;j<=m;j++)
                f[0][i][j]=max(f[0][i][j-1],max(f[0][i-1][j],g[i][j]));
        for(int i=k;i<=n;i++)
            for(int j=m-k+1;j;j--)
                f[1][i][j]=max(f[1][i][j+1],max(f[1][i-1][j],g[i][j+k-1]));
        for(int i=n-k+1;i;i--)
            for(int j=k;j<=m;j++)
                f[2][i][j]=max(f[2][i][j-1],max(f[2][i+1][j],g[i+k-1][j]));
        for(int i=n-k+1;i;i--)
            for(int j=m-k+1;j;j--)
                f[3][i][j]=max(f[3][i][j+1],max(f[3][i+1][j],g[i+k-1][j+k-1]));
         
        for(int i=k;i<=n;i++)
        {
            int mx=0;
            for(int j=i;j<=n;j++)
                mx=max(mx,h[j]),e[i][j]=mx;
        }
        for(int i=k;i<=m;i++)
        {
            int mx=0;
            for(int j=i;j<=m;j++)
                mx=max(mx,c[j]),d[i][j]=mx;
        }  
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            { 
                if(i+k<=n&&i>=k&&j>=k&&j+k<=m)ans=max(ans,f[0][i][j]+f[1][i][j+1]+e[i+k][n]);
                if(i+k-1<=n&&i>k&&j>k&&j+k-1<=m) ans=max(ans,f[2][i][j-1]+f[3][i][j]+e[k][i-1]);
                if(i+k<=n&&i>=k&&j>=k&&j+k<=m) ans=max(ans,f[0][i][j]+f[2][i+1][j]+d[j+k][m]);
                if(i+k<=n&&i>=k&&j>k&&j+k-1<=m)ans=max(ans,f[1][i][j]+f[3][i+1][j]+d[k][j-1]);
            }
        for(int i=k+1;i<=n;i++)
            for(int j=i+k-1;j+k<=n;j++)
                ans=max(ans,e[k][i-1]+e[i+k-1][j]+e[j+k][n]);
        for(int i=k+1;i<=m;i++)
            for(int j=i+k-1;j+k<=m;j++)
                ans=max(ans,d[k][i-1]+d[i+k-1][j]+d[j+k][m]);
        cout<<ans;
        return 0;
    }

    B.会议中心

    活动安排问题,但是要求选的活动最多的情况下字典序最小。 n<=200000

    题解:

    按照字典序从小到大,当一个线段[l,r]插入时,找到它的前驱后继[ll,rr]。只有当[ll,l)的方案数+(r,rr]的方案数+1恰好等于[ll,rr]的方案数的时候,它才能成为最优解,那么就把它选入答案。

    怎样快速算出一个区间答案呢?倍增。我们离散后,用f[i][k]表示第i个点向后走2^k个区间最少到达哪里,预处理好后每次查询只要log.

    复杂度nlogn

    如果用set,代码就很短啦,但是我不想用set,练习一下平衡树吧。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define INF 2000000000
    #define MN 200000
    #define ML 18
    using namespace std;
    inline int read()
    {
        int x = 0 , f = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
        while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
        return x * f;
    }
    
    int fa[MN*4+5],c[MN*4+5][2],size[MN*4+5],nn[MN*4+5],q[MN*4+5],top,sign=0,rt=0,cc=0;
    int f[MN*2+5][ML+2];
    int n,l[MN+5],r[MN+5],l2[MN*2+5],tot=0,cnt=1;
    struct cus{int l,r,num;}s[MN+5];
    bool cmp(cus x,cus y){return x.r<y.r||(x.r==y.r&&x.l>y.l);}
    
    void ins(int&x,int k,int last)
    {
        if(!x){x=++cc;fa[x]=last;nn[x]=k;size[x]=1;return;}
        if(k<=nn[x]) ins(c[x][0],k,x);
        else ins(c[x][1],k,x);
        size[x]=size[c[x][0]]+size[c[x][1]]+1;
        if(max(size[c[x][0]],size[c[x][1]])>0.7*size[x]) sign=x;
    }
    
    void dfs(int x)
    {
        if(c[x][0]) dfs(c[x][0]);
        q[++top]=x;
        if(c[x][1]) dfs(c[x][1]);
        fa[x]=c[x][0]=c[x][1]=0;
    }
    
    void build(int x,int l,int r,int last)
    {
        if(l>r){x=0;return;}int mid=l+r>>1;
        x=q[mid];fa[x]=last;
        build(c[x][0],l,mid-1,x);
        build(c[x][1],mid+1,r,x);
        size[x]=size[c[x][0]]+size[c[x][1]]+1;
    }
    
    void rebuild()
    {
        int y=fa[sign];top=0;dfs(sign);
        if(!y) build(rt,1,top,0);
        else build(c[y][c[y][1]==sign],1,top,y);
        sign=0;
    }
    
    int query(int x,int rk)
    {
        if(!x) return 0;
        if(nn[x]<=rk) return size[c[x][0]]+1+query(c[x][1],rk);
        else return query(c[x][0],rk);
    }
    
    int ask_before(int x,int rk)
    {
        if(!x) return 0;int q;
        if(nn[x]<rk) return (q=ask_before(c[x][1],rk))?q:nn[x];
        else return ask_before(c[x][0],rk);
    }
    
    int ask_after(int x,int rk)
    {
        if(!x) return 0;int q;
        if(nn[x]>rk) return (q=ask_after(c[x][0],rk))?q:nn[x];
        else return ask_after(c[x][1],rk);
    }
    
    int calc(int l,int r)
    {
        int sum=0;
        for(int i=ML;i>=0;i--)
            if(f[l][i]<=r+1)
                l=f[l][i],sum+=1<<i;
    //    cout<<"calc"<<l<<" "<<r<<" "<<sum<<endl;
        return sum;
    }
    
    inline bool check(int l,int r)
    {
        int x=query(rt,l-1),y=query(rt,r);
    //    cout<<"check"<<l<<" "<<r<<" "<<x<<" "<<y<<endl;
        return query(rt,l-1)==query(rt,r)&&(y&1);
    }
    
    int main()
    {    
        n=read();
        for(int i=1;i<=n;i++)
            s[i].l=l[i]=read(),s[i].r=r[i]=read(),s[i].num=i;
        for(int i=1;i<=n;i++)
                l2[++tot]=s[i].l,l2[++tot]=s[i].r;
        sort(l2+1,l2+tot+1);
        for(int i=2;i<=tot;i++) 
            if(l2[i]!=l2[i-1])
                l2[++cnt]=l2[i];    
        memset(f,127,sizeof(f));;
        for(int i=1;i<=n;i++)
        {
            l[i]=lower_bound(l2,l2+cnt+1,l[i])-l2;
            r[i]=lower_bound(l2,l2+cnt+1,r[i])-l2;
            f[l[i]][0]=min(f[l[i]][0],r[i]+1);
        }
        for(int i=cnt;i>=0;i--)
        {
            f[i][0]=min(f[i][0],f[i+1][0]);
            for(int k=1;k<=ML;k++)
                if(f[i][k-1]<INF)
                    f[i][k]=f[f[i][k-1]][k-1];
        //    for(int k=0;k<=4;k++)
            //    cout<<i<<" "<<k<<" "<<f[i][k]<<endl; 
        }
        ins(rt,0,0);ins(rt,INF,0);
        printf("%d
    ",calc(1,INF));bool yes=false;
        for(int i=1;i<=n;i++)
        {
            if(!check(l[i],r[i])) continue;
            int ll=ask_before(rt,l[i]),rr=ask_after(rt,r[i]);
            if(calc(ll,l[i]-1)+calc(r[i]+1,rr)+1!=calc(ll,rr)) continue;
            ins(rt,l[i],0);ins(rt,r[i],0);
            if(yes)printf(" ");else yes=true;
            printf("%d",i);
         }
        return 0;
    }

    C.Atm

    这道题是我以前做的.....

    http://www.cnblogs.com/FallDream/p/bzoj1179.html

  • 相关阅读:
    PHP正则表达式入门教程[转]
    微信公众平台消息接口开发(6)电话号码链接与网址链接
    微信公众平台消息接口开发(7)快递查询
    server后台程序的内存使用问题
    支持向量机(SVM)(三)-- 最优间隔分类器(optimal margin classifier)
    NYOJ-451-光棍节的快乐
    为什么我们想法非常多,付诸行动的却非常少?
    ios两视图间托付(delegate)传值
    Kettle变量和自己定义java代码的实例应用
    測试Service
  • 原文地址:https://www.cnblogs.com/FallDream/p/apio2009.html
Copyright © 2020-2023  润新知