APIO2007-2015题解大集合(2009年篇)
2016-04-14 23:23
246 查看
采油区域【上古预警】
这个题我半年前拿到是一脸懵逼的,当时调了一中午才调出来……此题简直跟NOIP2002矩形覆盖是一个感觉,都具有超级复杂的讨论……这种题一个非常重要的辅助手段就是前缀和——对于矩形问题,是二维前缀和(虽然说相信NOIP提高组能拿300分以上的同学都知道二维前缀和是什么……);但是此题要预处理4个方向的前缀和,这确实不太多见。总之先预处理出4个角的二维前缀和(左上,左下,右上,右下),然后考虑这3个矩形的摆放情况。这3个矩形有6种摆放方式,分别是:円,円翻转90度,円翻转180度,円翻转270度,四和目。(中华文化博大精深,用汉字就可以省去画图的麻烦……不过看不懂的同学还是照着汉字画个图更好)
对于前4种情况,枚举两维上的分割线处理即可;对于第5、6种情况,则需要预处理出行与列夹住的中间区域的最大值。
#include<iostream> #include<iomanip> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; long long n,m,k,ans=0,map[1505][1505]={0},he[1505][1505]={0}; long long leftup[1505][1505]={0},leftdown[1505][1505]={0},rightup[1505][1505]={0},rightdown[1505][1505]={0}; long long hang[1505]={0},lie[1505]={0},row[1505][1505]={0},column[1505][1505]={0}; void input() {scanf("%lld%lld%lld",&n,&m,&k); for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) scanf("%lld",&map[i][j]); for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) he[i][j]=map[i][j]+he[i][j-1]+he[i-1][j]-he[i-1][j-1]; } void prepare() {for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) {leftup[i][j]=max(leftup[i][j-1],leftup[i-1][j]); if(i>=k&&j>=k)leftup[i][j]=max(leftup[i][j],he[i][j]-he[i-k][j]-he[i][j-k]+he[i-k][j-k]); } for(long long i=1;i<=n;i++) for(long long j=m;j>=1;j--) {rightup[i][j]=max(rightup[i][j+1],rightup[i-1][j]); if(i>=k&&j+k-1<=m)rightup[i][j]=max(rightup[i][j],he[i][j+k-1]-he[i-k][j+k-1]-he[i][j-1]+he[i-k][j-1]); } for(long long i=n;i>=1;i--) for(long long j=1;j<=m;j++) {leftdown[i][j]=max(leftdown[i][j-1],leftdown[i+1][j]); if(i+k-1<=m&&j>=k)leftdown[i][j]=max(leftdown[i][j],he[i+k-1][j]-he[i+k-1][j-k]-he[i-1][j]+he[i-1][j-k]); } for(long long i=n;i>=1;i--) for(long long j=m;j>=1;j--) {rightdown[i][j]=max(rightdown[i][j+1],rightdown[i-1][j]); if(i+k-1<=m&&j+k-1<=m)rightdown[i][j]=max(rightdown[i][j],he[i+k-1][j+k-1]-he[i-1][j+k-1]-he[i+k-1][j-1]+he[i-1][j-1]); } for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) if(i>=k&&j>=k) {hang[i]=max(hang[i],he[i][j]-he[i-k][j]-he[i][j-k]+he[i-k][j-k]); lie[j]=max(lie[j],he[i][j]-he[i-k][j]-he[i][j-k]+he[i-k][j-k]); } for(long long i=1;i<=n;i++) for(long long j=1;j+k-1<=i;j++) {row[j][i]=hang[i]; if(j>1)row[j][i]=max(row[j][i],row[j-1][i]); } for(long long i=1;i<=m;i++) for(long long j=1;j+k-1<=i;j++) {column[j][i]=lie[i]; if(j>1)column[j][i]=max(column[j][i],column[j-1][i]); } for(long long i=1;i<=n;i++)//第五种情况:目 for(long long j=1;j<i;j++) {if(i<k||j<k||i+k>n||i-j+1<k)continue; ans=max(ans,leftup[j][m]+leftdown[i+1][m]+row[j+1][i]); } for(long long i=1;i<=m;i++)//第六种情况:四 for(long long j=1;j<i;j++) {if(i<k||j<k||i+k>m||i-j+1<k)continue; ans=max(ans,leftup [j]+rightup [i+1]+column[j+1][i]); } } void solve1()//先处理4种情况 {for(long long i=1;i<=n;i++) for(long long j=1;j<=m;j++) {if(i>=k&&j>=k&&j+k<=m&&i+k<=m) {ans=max(ans,leftup[i][j]+rightup[i][j+1]+leftdown[i+1][m]); ans=max(ans,leftup [j]+rightup[i][j+1]+rightdown[i+1][j+1]); ans=max(ans,leftup[i][m]+leftdown[i+1][j]+rightdown[i+1][j+1]); ans=max(ans,leftup[i][j]+rightup [j+1]+leftdown[i+1][j]); } } } int main() {input(); prepare(); solve1(); printf("%lld",ans); return 0; }
会议中心
首先如果不考虑输出字典序最小解,则此题即普及组贪心水题。现在有字典序问题,我们考虑在求出答案后枚举加入边。(这是一种常用思想,类似的思想见于求字典序最小的最小割。)
不难想到,在最优方案下,每条线段接下来该取哪条是确定的。这时我们为了确定整个序列,就考虑倍增。(类似的思想见于NOIP2012开车旅行。)于是我
们设f[i,j]表示第i条线段向后取2^j条线段的最小右端点,这可以通过倍增求出。于是我们有了快速求出一个给定坐标范围内答案的方法:从左端点开始,从长到短
试着跳,如果能跳,则跳到该右端点+1除处,同时把对应跳过的2^j条线段累加入答案。现在我们从小到大枚举(这样能够保证之前放下的线段优先级必然高于
之后),用一个set维护当前解集:首先在set中二分找出离当前考虑的线段端点最接近的两个区间,若它们之间直接的答案等于被中间隔断的两端区间答案之和
+1,则当前区间合法,放入set,最后输出set中元素即可。另外此题不要去除包含的区间——有可能会漏掉字典序更小解。
<span style="font-size:12px;font-weight: normal;">#include<iostream>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<set>
using namespace std;
int n,tot,stage,top,lisan[400005],nxt[400005][22]={0},c[400005]={0};
struct node{int st,ed,id;}line[400005],stk[400005];
set<node>s;
bool operator<(node x,node y)
{return x.st<y.st||(x.st==y.st&&x.ed<y.ed);
}
bool Comp2(node x,node y)
{return x.id<y.id;
}
int Lowbit(int i){return i&(-i);}
void Add(int x,int v)
{for(int i=x;i<=tot;i+=Lowbit(i))c[i]+=v;
}
int Sum(int x)
{int ret=0;
for(int i=x;i>=1;i-=Lowbit(i))
ret+=c[i];
return ret;
}
void Input()
{lisan[0]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{scanf("%d%d",&line[i].st,&line[i].ed);
lisan[++lisan[0]]=line[i].st,lisan[++lisan[0]]=line[i].ed;
line[i].id=i;
}
sort(lisan+1,lisan+lisan[0]+1);
tot=unique(lisan+1,lisan+lisan[0]+1)-lisan-1,top=0;
stage=int(log(tot)/log(2))+1;
sort(line+1,line+n+1);
for(int i=1;i<=n;i++)
{line[i].st=lower_bound(lisan+1,lisan+tot+1,line[i].st)-lisan;
line[i].ed=lower_bound(lisan+1,lisan+tot+1,line[i].ed)-lisan;
}
}
void St()
{for(int i=1;i<=tot+1;i++)nxt[i][0]=999999999;
for(int i=1;i<=n;i++)nxt[line[i].st][0]=line[i].ed;//这里好像有些不对。。。
for(int i=tot-1;i>=1;i--)nxt[i][0]=min(nxt[i+1][0],nxt[i][0]);
for(int j=1;j<=stage;j++)
{nxt[tot+1][j]=999999999;
for(int i=1;i<=tot;i++)
{if(nxt[i][j-1]!=999999999)
nxt[i][j]=nxt[nxt[i][j-1]+1][j-1];
else nxt[i][j]=999999999;
}
}
}
int Ans(int l,int r)
{int ret=0,now=l;
if(l>r)return 0;
for(int j=stage;j>=0;j--)
{if(nxt[now][j]<=r)ret+=(1<<j),now=nxt[now][j]+1;
}
return ret;
}
void Solve()
{int wer=Ans(1,tot);
sort(line+1,line+n+1,Comp2);
printf("%d\n",wer);
for(int i=1;i<=n;i++)
{int L,R,ans1,ans2;
if(Sum(line[i].ed)-Sum(line[i].st-1)>0)continue;
set<node>::iterator it=s.upper_bound(line[i]);
if(it==s.end()) R=tot;
else R=(*it).st-1;
ans2=Ans(line[i].ed+1,R);
if(it==s.begin()) L=1;
else {it--;L=(*it).ed+1;}
ans1=Ans(L,line[i].st-1);
if(ans1+ans2+1==Ans(L,R))
{printf("%d ",i);
s.insert(line[i]);
for(int j=line[i].st;j<=line[i].ed;j++)Add(j,1);
}
}
}
int main()
{Input();
St();
Solve();
return 0;
}
/*
5
9 17
20 30
10 15
1 9
10 16
*/</span>
抢掠计划【上古预警】
NOIP标准图论题,先进行强连通分量缩点,然后把每个SCC看成一个点重新建图,边权为边指向的联通块拥有的现金和,最后用spfa或者拓扑序递推求一次从开始点到结束点最长路,对酒吧们取max就好。此题唯一的坑点在于:需要手工栈。
#include<iostream> #include<iomanip> #include<algorithm> #include<cstring> #include<cstdio> #include<cstdlib> #include<cmath> using namespace std; //基本思路:先kosaraju然后从1所在强连通分量出发找通往每个酒吧的最长路最后一扫便知。 long long BCC=0,cnt=0,ans=0,top=0,st,p,fcnt=0,newcnt=0,jishu=0,n,m,a,b,d[500005]={0},h[500005]={0},fh[500005]={0}; long long vis[500005]={0},money[500005]={0},newh[500005]={0},queue[500005]={0},dist[500005]={0},inqueue[500005]={0}; bool bar[500005]={0}; struct node{long long next,to;}edge[1000005],fedge[1000005]; struct nodf{long long next,to,v;}newedge[1000005]; struct nodg{long long x,i;}stk[600005]; long long getint() {long long ret=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while('0'<=ch&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return ret*f; } void addedge(long long x,long long y) {cnt++,edge[cnt].to=y,edge[cnt].next=h[x],h[x]=cnt; fcnt++,fedge[fcnt].to=x,fedge[fcnt].next=fh[y],fh[y]=fcnt; } void addnewedge(long long x,long long y,long long z) {newcnt++,newedge[newcnt].v=z,newedge[newcnt].to=y,newedge[newcnt].next=newh[x],newh[x]=newcnt; } void input() {n=getint(),m=getint(); for(long long i=1;i<=m;i++) {a=getint(),b=getint(); addedge(a,b); } } void dfs1(long long x) {vis[x]=1; for(long long i=h[x];i;i=edge[i].next) {long long y=edge[i].to; if(!vis[y])dfs1(y); } d[++jishu]=x; } void dfs1_manual(long long x) {int i,y;top=0; stk[++top].x=x,stk[top].i=h[x]; START:; x=stk[top].x,i=stk[top].i; vis[x]=1; for(i=h[x];i;i=edge[i].next) {y=edge[i].to; if(!vis[y]) {stk[top].i=i,stk[++top].x=y; goto START; } MIDDLE:; } d[++jishu]=x; if(top>1) {top--; i=stk[top].i,x=stk[top].x; goto MIDDLE; } } void dfs2(long long x) {vis[x]=BCC; for(long long i=fh[x];i;i=fedge[i].next) {long long y=fedge[i].to; if(!vis[y])dfs2(y); } } void dfs2_manual(long long x) {int i,y;top=0; stk[++top].x=x,stk[top].i=fh[x]; ANOTHERSTART:; x=stk[top].x,i=stk[top].i; vis[x]=BCC; for(i=fh[x];i;i=fedge[i].next) {y=fedge[i].to; if(!vis[y]) {stk[top].i=i,stk[++top].x=y; goto ANOTHERSTART; } ANOTHERMIDDLE:; } if(top>1) {top--; i=stk[top].i,x=stk[top].x; goto ANOTHERMIDDLE; } } void kosaraju() { if(n<32767) {for(long long i=1;i<=n;i++) if(!vis[i])dfs1(i); memset(vis,0,sizeof(vis)); for(long long i=n;i>=1;i--) if(!vis[d[i]])BCC++,dfs2(d[i]); } else {for(long long i=1;i<=n;i++) if(!vis[i])dfs1_manual(i); memset(vis,0,sizeof(vis)); for(long long i=n;i>=1;i--) if(!vis[d[i]])BCC++,dfs2_manual(d[i]); } } void buildgraph() {for(long long i=1;i<=n;i++) {a=getint(); money[vis[i]]+=a; } for(long long i=1;i<=n;i++) {for(long long j=h[i];j;j=edge[j].next) {long long y=edge[j].to; if(vis[y]!=vis[i])addnewedge(vis[i],vis[y],money[vis[y]]);//现在是DAG了,不用担心正环 } } st=getint(),p=getint(); st=vis[st]; for(long long i=1;i<=p;i++) {a=getint(); bar[vis[a]]=1; } } void spfa()//注意是最长路! {long long head=0,tail=1; for(long long i=1;i<=BCC;i++)dist[i]=-999999999; queue[tail]=st,inqueue[st]=1,dist[st]=money[st];//注意不是0! while(head!=tail) {head=(head+1)%500003; long long x=queue[head]; inqueue[x]=0; for(long long i=newh[x];i;i=newedge[i].next) {long long y=newedge[i].to; if(dist[x]+newedge[i].v>dist[y]) {dist[y]=dist[x]+newedge[i].v; if(!inqueue[y]) {tail=(tail+1)%500003; queue[tail]=y; inqueue[y]=1; } } } } } void solve() {for(long long i=1;i<=BCC;i++) if(bar[i])ans=max(ans,dist[i]); printf("%lld\n",ans); } int main() { input(); kosaraju(); buildgraph(); spfa(); solve(); fclose(stdin); fclose(stdout); return 0; } /* 6 7 1 2 2 3 1 6 6 3 3 5 5 6 2 4 25 30 17 75 21 33 4 2 4 6 */
相关文章推荐
- [转]F5负载均衡算法及基本原理
- 实验二
- 实例 18错误输出信息与调试信息
- 安卓逆向工程--针对授权key方式的破解
- 自然语言处理实战之微博情感偏向分析
- 腾讯实习生面试题--替换字符串中指定子串
- InfoPath错误,此文档库已被重命名或删除
- 四则运算
- struts1中使用Validator插件对表单进行校验
- 2.1.2 个人环境的使用
- 页面跳转与重定向
- java实现随机洗牌算法
- nginx+nginx-rtmp-module+ffmpeg搭建流媒体服务器
- [C#]大小端字节序(Big Endian和Little Endian)
- css 伪元素的初次使用
- C# 解决 调用线程非安全的控件xx不是在本线程上创建 异常
- jstat监控JVM内存使用情况、GC回收情况
- android:dp,px,sp概念及之间转换
- 四则运算+判断
- 【深度学习】web版一键训练系统