[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

时间: 2024-11-08 13:07:13

[APIO2009]的相关文章

【BZOJ 1177】 [Apio2009]Oil

1177: [Apio2009]Oil Time Limit: 15 Sec  Memory Limit: 162 MB Submit: 1044  Solved: 404 [Submit][Status][Discuss] Description 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M×N个小块. Siruseri地质调查局有关于Navalur土地石油储量的估测数据.这些数据表示为M×N个非

1179: [Apio2009]Atm

1179: [Apio2009]Atm Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1629  Solved: 615[Submit][Status] Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S

bzoj1179: [Apio2009]Atm

tarjan缩点就是DAG上求最长路把...然而我并不会求...只会写spfa了... #include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<stack> #include<queue> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #defin

BZOJ1179 : [Apio2009]Atm 缩点+spfa

1179: [Apio2009]Atm Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 2069  Solved: 826[Submit][Status][Discuss] Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来

BZOJ 1179: [Apio2009]Atm( tarjan + 最短路 )

对于一个强连通分量, 一定是整个走或者不走, 所以tarjan缩点然后跑dijkstra. --------------------------------------------------------------------- #include<bits/stdc++.h> #define rep(i, n) for(int i = 0; i < n; ++i) #define clr(x, c) memset(x, c, sizeof(x)) #define foreach(i,

tarjan+spfa最短路 BZOJ1179 [Apio2009] Atm

1179: [Apio2009]Atm Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 3641  Solved: 1552[Submit][Status][Discuss] Description Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下

[BZOJ1179][APIO2009][强连通分量Tarjan+spfa]ATM

[BZOJ1179][APIO2009]ATM Input 第一行包含两个整数N.M.N表示路口的个数,M表示道路条数.接下来M行,每行两个整数,这两个整数都在1到N之间,第i+1行的两个整数表示第i条道路的起点和终点的路口编号.接下来N行,每行一个整数,按顺序表示每个路口处的ATM机中的钱数.接下来一行包含两个整数S.P,S表示市中心的编号,也就是出发的路口.P表示酒吧数目.接下来的一行中有P个整数,表示P个有酒吧的路口的编号 Output 输出一个整数,表示Banditji从市中心开始到某个

【Tarjan】+【SPFA】【APIO2009】Atm

一.算法介绍 tarjan——求解有向图强连通分量.这个算法在本人的一篇blog中有介绍,这里就不赘述了.贴上介绍tarjan的的blog链接:http://www.cnblogs.com/Maki-Nishikino/p/5866191.html 那么接下来说说SPFA: SPFA全称Shortest Path Faster Algorithm,用于求解单源最短路.既然名字中有“Faster”,那它就一定有过人之处,事实上它也的确比Dijkstra和Bellman-Ford更高效. 它的思路大

【BZOJ 1178】 [Apio2009]CONVENTION会议中心

1178: [Apio2009]CONVENTION会议中心 Time Limit: 15 Sec Memory Limit: 162 MB Submit: 539 Solved: 209 [Submit][Status][Discuss] Description Siruseri政府建造了一座新的会议中心.许多公司对租借会议中心的会堂很感兴趣,他们希望能够在里面举行会议. 对于一个客户而言,仅当在开会时能够独自占用整个会堂,他才会租借会堂.会议中心的销售主管认为:最好的策略应该是将会堂租借给尽

[Bzoj 1177][Apio2009] Oil 前缀和+递推

1177: [Apio2009]Oil Time Limit: 15 Sec Memory Limit: 162 MB Submit: 1569 Solved: 632 [Submit][Status][Discuss] Description 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M×N个小块. Siruseri地质调查局有关于Navalur土地石油储量的估测数据.这些数据表示为M×N个非负整