[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;
}