[SDOI2010]所驼门王的宝藏

https://www.zybuluo.com/ysner/note/1250991

题面

有一个\(n*m\)的网格图,其中有\(tot\)个有门+宝藏的网格。这些门可以传送,且分为三类:

  • 门一:可到同行的任意一格
  • 门二:可到同列的任意一格
  • 门三:可到相邻的八格。

从任意点出发和终止,最多能经过多少有宝藏的网格。

  • \(tot\leq10^5,n,m\leq10^6\)

解析

首先可以考虑到的一个问题是,建边不能达到\(O(n^2)\)
然而,如果门一都在同行,门二都在同列,就可以达到。
考虑把同行的门一、同列的门二用环连起来
这样效果等价(强连通),建边复杂度降到\(O(n)\)
然后再暴力连同行同列剩下的门(用环中的一点)和门三连的有宝藏网格即可。

这样弄完后,缩点+拓扑排序后跑最长路即可。(边权为联通块大小)

然而细节非常容易出锅:
1、如果\(vector\)中无值,访问\(front()\)\(back()\)\(RE\)
(其实最好把它当一般数组用)
2、\(tarjan\)不需要判\(v!=fa\)
3、如果有两张图,且把\(e\)\(e1\)搞混,就会莫名\(TLE\)
4、如果使用\(unordered\_map\)判重结构体,需要重载\(==\)运算符并手写\(hash\)函数。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<tr1/unordered_map>
#define ll long long
#define re register
#define il inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=1e5+100;
struct Edge{int to,nxt;}e[N*50];
struct node{int x,y,id;bool operator < (const node &o) const {return (x<o.x)||(x==o.x&&y<o.y);}}a[N];
struct nnode{int x,y;bool operator == (const nnode &o) const {return x==o.x&&y==o.y;}}p[N*10];
struct Hash{
  std::size_t operator () (const nnode &o) const{return o.x*N*10+o.y;};
};
int n,m,tot,h[N],cnt,fx[9]={0,1,-1,0,0,1,-1,1,-1},fy[9]={0,0,0,1,-1,1,-1,-1,1},low[N],dfn[N],st[N],tt,tim,sz[N],scc,bl[N],dis[N],T,in[N],ans,tmp[N];
bool vis[N];
vector<int>stan[N*10],stam[N*10];
queue<int>Q;
std::tr1::unordered_map<nnode,int,Hash>mp;
il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
il int gi()
{
    re int x=0,t=1;
    re char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') t=-1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*t;
}
il void tarjan(re int u)
{
  low[u]=dfn[u]=++tim;vis[u]=1;st[++tt]=u;
  re int v;
  for(re int i=h[u];i+1;i=e[i].nxt)
    {
      v=e[i].to;
      if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
      else if(vis[v]) low[u]=min(low[u],dfn[v]);
    }
  if(low[u]==dfn[u])
    {
      ++scc;
      do{
    v=st[tt];vis[v]=0;bl[v]=scc;sz[scc]++;tt--;
      }
      while(v!=u);
    }
}
int main()
{
  memset(h,-1,sizeof(h));
  tot=gi();n=gi();m=gi();
  fp(i,1,tot) a[i].x=gi(),a[i].y=gi(),a[i].id=gi(),mp[(nnode){a[i].x,a[i].y}]=i;
  fp(i,1,tot)
    {
      stan[a[i].x].push_back(i);
      stam[a[i].y].push_back(i);
      if(a[i].id==3)
      fp(j,1,8)
        {
          re int xx=a[i].x+fx[j],yy=a[i].y+fy[j];
          if(xx<1&&xx>n&&yy<1&&yy>m) continue;
          if(mp[(nnode){xx,yy}]) add(i,mp[(nnode){xx,yy}]);
        }
    }
  fp(i,1,n)
  {
    tt=0;
    for(re int j=0;j<stan[i].size();j++) 
      if(a[stan[i][j]].id==1) tmp[++tt]=stan[i][j];
    fp(j,1,tt-1) add(tmp[j],tmp[j+1]);add(tmp[tt],tmp[1]);
    for(re int j=0;j<stan[i].size();j++) 
      if(a[stan[i][j]].id!=1&&tt) add(tmp[1],stan[i][j]);
  }
  fp(i,1,m)
  {
    tt=0;
    for(re int j=0;j<stam[i].size();j++) 
      if(a[stam[i][j]].id==2) tmp[++tt]=stam[i][j];
    fp(j,1,tt-1) add(tmp[j],tmp[j+1]);add(tmp[tt],tmp[1]);
    for(re int j=0;j<stam[i].size();j++) 
      if(a[stam[i][j]].id!=2&&tt) add(tmp[1],stam[i][j]);
  }
  tt=0;
  fp(i,1,tot) if(!dfn[i]) tarjan(i);
  fp(u,1,tot)
    for(re int i=h[u];i+1;i=e[i].nxt)
      {
    re int v=e[i].to;
    if(bl[u]!=bl[v]) p[++T]=(nnode){bl[u],bl[v]};
      }
  memset(h,-1,sizeof(h));cnt=0;
  fp(i,1,T) add(p[i].x,p[i].y),in[p[i].y]++;
  fp(i,1,scc) if(!in[i]) Q.push(i);
  while(!Q.empty())
    {
      re int u=Q.front();Q.pop();dis[u]=max(dis[u],sz[u]);
      for(re int i=h[u];i+1;i=e[i].nxt)
    {
      re int v=e[i].to;--in[v];
      if(in[v]==0) Q.push(v);
      dis[v]=max(dis[v],dis[u]+sz[v]);
      ans=max(ans,dis[v]);
    }
   }
  printf("%d\n",ans);
  return 0;
}
posted @ 2018-08-16 08:00  小蒟蒻ysn  阅读(189)  评论(0编辑  收藏  举报