APIO2007-2015题解大集合(2009年篇)

采油区域【上古预警】

           这个题我半年前拿到是一脸懵逼的,当时调了一中午才调出来……此题简直跟NOIP2002矩形覆盖是一个感觉,都具有超级复杂的讨论……
         这种题一个非常重要的辅助手段就是前缀和——对于矩形问题,是二维前缀和(虽然说相信NOIP提高组能拿300分以上的同学都知道二维前缀和是什么……);但是此题要预处理4个方向的前缀和,这确实不太多见。总之先预处理出4个角的二维前缀和(左上,左下,右上,右下),然后考虑这3个矩形的摆放情况。这3个矩形有6种摆放方式,分别是:円,円翻转90度,円翻转180度,円翻转270度,四和目。(中华文化博大精深,用汉字就可以省去画图的麻烦……不过看不懂的同学还是照着汉字画个图更好)
       
        对于前4种情况,枚举两维上的分割线处理即可;对于第5、6种情况,则需要预处理出行与列夹住的中间区域的最大值。
         
#include
#include
#include
#include
#include
#include
#include
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;jn||i-j+1m||i-j+1=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[n][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[n][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中元素即可。另外此题不要去除包含的区间——有可能会漏掉字典序更小解。

    

#include
#include
#include
#include
#include
#include
#include
#include
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];
sets;
bool operator<(node x,node y)
  {return x.st=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<0)continue;
	  set::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
*/



 抢掠计划【上古预警】

    NOIP标准图论题,先进行强连通分量缩点,然后把每个SCC看成一个点重新建图,边权为边指向的联通块拥有的现金和,最后用spfa或者拓扑序递推求一次从

开始点到结束点最长路,对酒吧们取max 就好。此题唯一的坑点在于:需要手工栈。
   
#include
#include
#include
#include
#include
#include
#include
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
*/

你可能感兴趣的:(APIO2007-2015题解大集合(2009年篇))