来自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 = , f = ; char ch = getchar();
while(ch < '' || ch > ''){ if(ch == '-') f = -; ch = getchar();}
while(ch >= '' && ch <= ''){x = x * + ch - '';ch = getchar();}
return x * f;
} int n,m,k,ans=;
int f[][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]):;} int main()
{
//freopen("test0.in","r",stdin);
n=read();m=read();k=read();
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
scanf("%d",&s[i][j]);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
s[i][j]=s[i][j]+s[i-][j]+s[i][j-]-s[i-][j-];
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[][i][j]=max(f[][i][j-],max(f[][i-][j],g[i][j]));
for(int i=k;i<=n;i++)
for(int j=m-k+;j;j--)
f[][i][j]=max(f[][i][j+],max(f[][i-][j],g[i][j+k-]));
for(int i=n-k+;i;i--)
for(int j=k;j<=m;j++)
f[][i][j]=max(f[][i][j-],max(f[][i+][j],g[i+k-][j]));
for(int i=n-k+;i;i--)
for(int j=m-k+;j;j--)
f[][i][j]=max(f[][i][j+],max(f[][i+][j],g[i+k-][j+k-])); for(int i=k;i<=n;i++)
{
int mx=;
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=;
for(int j=i;j<=m;j++)
mx=max(mx,c[j]),d[i][j]=mx;
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
{
if(i+k<=n&&i>=k&&j>=k&&j+k<=m)ans=max(ans,f[][i][j]+f[][i][j+]+e[i+k][n]);
if(i+k-<=n&&i>k&&j>k&&j+k-<=m) ans=max(ans,f[][i][j-]+f[][i][j]+e[k][i-]);
if(i+k<=n&&i>=k&&j>=k&&j+k<=m) ans=max(ans,f[][i][j]+f[][i+][j]+d[j+k][m]);
if(i+k<=n&&i>=k&&j>k&&j+k-<=m)ans=max(ans,f[][i][j]+f[][i+][j]+d[k][j-]);
}
for(int i=k+;i<=n;i++)
for(int j=i+k-;j+k<=n;j++)
ans=max(ans,e[k][i-]+e[i+k-][j]+e[j+k][n]);
for(int i=k+;i<=m;i++)
for(int j=i+k-;j+k<=m;j++)
ans=max(ans,d[k][i-]+d[i+k-][j]+d[j+k][m]);
cout<<ans;
return ;
}

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 = , f = ; char ch = getchar();
while(ch < '' || ch > ''){ if(ch == '-') f = -; ch = getchar();}
while(ch >= '' && ch <= ''){x = x * + ch - '';ch = getchar();}
return x * f;
} int fa[MN*+],c[MN*+][],size[MN*+],nn[MN*+],q[MN*+],top,sign=,rt=,cc=;
int f[MN*+][ML+];
int n,l[MN+],r[MN+],l2[MN*+],tot=,cnt=;
struct cus{int l,r,num;}s[MN+];
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]=;return;}
if(k<=nn[x]) ins(c[x][],k,x);
else ins(c[x][],k,x);
size[x]=size[c[x][]]+size[c[x][]]+;
if(max(size[c[x][]],size[c[x][]])>0.7*size[x]) sign=x;
} void dfs(int x)
{
if(c[x][]) dfs(c[x][]);
q[++top]=x;
if(c[x][]) dfs(c[x][]);
fa[x]=c[x][]=c[x][]=;
} void build(int x,int l,int r,int last)
{
if(l>r){x=;return;}int mid=l+r>>;
x=q[mid];fa[x]=last;
build(c[x][],l,mid-,x);
build(c[x][],mid+,r,x);
size[x]=size[c[x][]]+size[c[x][]]+;
} void rebuild()
{
int y=fa[sign];top=;dfs(sign);
if(!y) build(rt,,top,);
else build(c[y][c[y][]==sign],,top,y);
sign=;
} int query(int x,int rk)
{
if(!x) return ;
if(nn[x]<=rk) return size[c[x][]]++query(c[x][],rk);
else return query(c[x][],rk);
} int ask_before(int x,int rk)
{
if(!x) return ;int q;
if(nn[x]<rk) return (q=ask_before(c[x][],rk))?q:nn[x];
else return ask_before(c[x][],rk);
} int ask_after(int x,int rk)
{
if(!x) return ;int q;
if(nn[x]>rk) return (q=ask_after(c[x][],rk))?q:nn[x];
else return ask_after(c[x][],rk);
} int calc(int l,int r)
{
int sum=;
for(int i=ML;i>=;i--)
if(f[l][i]<=r+)
l=f[l][i],sum+=<<i;
// cout<<"calc"<<l<<" "<<r<<" "<<sum<<endl;
return sum;
} inline bool check(int l,int r)
{
int x=query(rt,l-),y=query(rt,r);
// cout<<"check"<<l<<" "<<r<<" "<<x<<" "<<y<<endl;
return query(rt,l-)==query(rt,r)&&(y&);
} int main()
{
n=read();
for(int i=;i<=n;i++)
s[i].l=l[i]=read(),s[i].r=r[i]=read(),s[i].num=i;
for(int i=;i<=n;i++)
l2[++tot]=s[i].l,l2[++tot]=s[i].r;
sort(l2+,l2+tot+);
for(int i=;i<=tot;i++)
if(l2[i]!=l2[i-])
l2[++cnt]=l2[i];
memset(f,,sizeof(f));;
for(int i=;i<=n;i++)
{
l[i]=lower_bound(l2,l2+cnt+,l[i])-l2;
r[i]=lower_bound(l2,l2+cnt+,r[i])-l2;
f[l[i]][]=min(f[l[i]][],r[i]+);
}
for(int i=cnt;i>=;i--)
{
f[i][]=min(f[i][],f[i+][]);
for(int k=;k<=ML;k++)
if(f[i][k-]<INF)
f[i][k]=f[f[i][k-]][k-];
// for(int k=0;k<=4;k++)
// cout<<i<<" "<<k<<" "<<f[i][k]<<endl;
}
ins(rt,,);ins(rt,INF,);
printf("%d\n",calc(,INF));bool yes=false;
for(int i=;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]-)+calc(r[i]+,rr)+!=calc(ll,rr)) continue;
ins(rt,l[i],);ins(rt,r[i],);
if(yes)printf(" ");else yes=true;
printf("%d",i);
}
return ;
}

C.Atm

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

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

[APIO2009]的更多相关文章

  1. 【Tarjan】+【SPFA】APIO2009 Atm

    一.算法介绍 tarjan——求解有向图强连通分量.这个算法在本人的一篇blog中有介绍,这里就不赘述了.贴上介绍tarjan的的blog链接:http://www.cnblogs.com/Maki- ...

  2. [BZOJ1177][Apio2009]Oil

    [BZOJ1177][Apio2009]Oil 试题描述 采油区域 Siruseri政府决定将石油资源丰富的Navalur省的土地拍卖给私人承包商以建立油井.被拍卖的整块土地为一个矩形区域,被划分为M ...

  3. 【BZOJ】【1177】【APIO2009】Oil

    DP 找出三个正方形,可以转化为将整个油田切成三个矩形块,每块中各找一个正方形区域,切的形式只有6种,分类更新ans即可 题解:http://trinklee.blog.163.com/blog/st ...

  4. 枚举(分类讨论):BZOJ 1177: [Apio2009]Oil

    1177: [Apio2009]Oil Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1477  Solved: 589[Submit] Descri ...

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

    对于一个强连通分量, 一定是整个走或者不走, 所以tarjan缩点然后跑dijkstra. ------------------------------------------------------ ...

  6. 1179: [Apio2009]Atm

    1179: [Apio2009]Atm Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 1629  Solved: 615[Submit][Status ...

  7. [APIO2009]会议中心

    [APIO2009]会议中心 题目大意: 原网址与样例戳我! 给定n个区间,询问以下问题: 1.最多能够选择多少个不相交的区间? 2.在第一问的基础上,输出字典序最小的方案. 数据范围:\(n \le ...

  8. BZOJ_1179_[Apio2009]Atm_tarjan+spfa

    BZOJ_1179_[Apio2009]Atm_tarjan+spfa 题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1179 分析: 显然有环 ...

  9. P3627 [APIO2009]抢掠计划

    P3627 [APIO2009]抢掠计划 Tarjan缩点+最短(最长)路 显然的缩点...... 在缩点时,顺便维护每个强连通分量的总权值 缩完点按照惯例建个新图 然后跑一遍spfa最长路,枚举每个 ...

  10. 【BZOJ】【1178】【APIO2009】convention会议中心

    贪心 如果不考虑字典序的话,直接按右端点排序,能选就选,就可以算出ans…… 但是要算一个字典序最小的解就比较蛋疼了= = Orz了zyf的题解 就是按字典序从小到大依次枚举,在不改变答案的情况下,能 ...

随机推荐

  1. 配置SpringAop时需要用到的AspectJ表达式

    Aspectj切入点语法定义 在使用spring框架配置AOP的时候,不管是通过XML配置文件还是注解的方式都需要定义pointcut"切入点" 例如定义切入点表达式  execu ...

  2. hdu 4553 约会安排

    约会安排 http://acm.hdu.edu.cn/showproblem.php?pid=4553 Time Limit: 2000/1000 MS (Java/Others)    Memory ...

  3. bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

    http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与 ...

  4. nyoj 复杂度

    复杂度 时间限制:1000 ms  |  内存限制:65535 KB 难度:3   描述 for(i=1;i<=n;i++) for(j=i+1;j<=n;j++) for(k=j+1;k ...

  5. service层报错找不到方法Invalid bound statement (not found)

    报错信息如下 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.imooc.se ...

  6. dubbo debug过程中一个有趣的问题

    最近在debug dubbo代码过程中遇到的很有趣的问题 我们都知道dubbo ReferenceBean是消费者的spring bean包装,为了查一个consumer端的问题,在Reference ...

  7. Spring知识点回顾(02)AOP

    一.注解拦截 二.方法规则拦截

  8. OAuth2.0学习(1-12)开源的OAuth2.0项目和比较

    OAuth2.0学习(2-1)OAuth的开源项目   1.开源项目列表 http://www.oschina.net/project/tag/307/oauth?lang=19&sort=t ...

  9. [CodeForces10D]LCIS(最长公共上升子序列) - DP

    Description 给定两个数列,求最长公共上升子序列,并输出其中一种方案. Input&Output Input 第一行一个整数n(0<n<=500),数列a的长度. 第二行 ...

  10. qt中文乱码

    刚开始学习qt,经常会遇到中文输出乱码,在网上找了解决办法有下面这个两个办法QTextCodec::setCodecForCStrings(QTextCodec::codecForName(" ...