[APIO2009]

Posted FallDream

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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\\n",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

 

以上是关于[APIO2009]的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ 1177 [Apio2009]Oil(递推)

BZOJ_1179_[Apio2009]Atm_tarjan+spfa

bzoj1178 Apio2009—CONVENTION会议中心

BZOJ 1179[Apio2009]Atm

BZOJ 1179: [Apio2009]Atm

[BZOJ1179] [Apio2009]Atm(tarjan缩点 + spfa)