思路还是简单的,代码就。。。。反正bzoj上比我快的都比我长(3k+)
显然,如果把每个点向它能到达的点连一条有向边,那么答案就是这个图的最长链(废话)。然后就是一个景点的强连通分量缩点+dp最长路。但这道题目不能直接暴力连,因为对于同一行的横天门,如果直接暴力连的话是O(N^2)的。实际上同一行的横天门,只需要连向一个切连双向边即可;另外,同一行的横天门如果直接向该行的其他种类的门连边也是O(N^2)的,同理只需要一个横天门向其它种类的门连边即可(因为前面的连边它们已经构成一个强连通分量了)。纵寰门同理。如果用排序会好写一点,但是用链表也可以做到O(N)。
另外,对于自由门,可以这样处理(暴力的话直接map就行了):按行来,对于每一行,统计前一行,当前行,后一行每一列是否有门;然后枚举这一行的所有自由门,根据前面的统计可以得到这个自由门周围8格是否有门,有的话连一条有向边。这样时间复杂度应该是O(R+N)(不排序的话),空间复杂度O(C)。
如果用排序实现,时间复杂度应该是O(NlogN+R),还是很快的。
AC代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 100005 using namespace std; struct node{ int x,y,z,id; }a[N],edg[N]; int cnt,tot,tp,dfsclk,edg_p,stk[N],scc[N],sum[N],dp[N]; int fst[N<<1],pnt[N<<2],nxt[N<<2],pos[N],low[N],val[N*5][3]; void add(int aa,int bb){ pnt[++tot]=bb; nxt[tot]=fst[aa]; fst[aa]=tot; } int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x; } bool cmp_r(node aa,node bb){ return (aa.x==bb.x)?aa.z<bb.z:aa.x<bb.x; } bool cmp_c(node aa,node bb){ return (aa.y==bb.y)?bb.z!=2 && (aa.z==2 || aa.z<bb.z):aa.y<bb.y; } bool cmp(node aa,node bb){ return (aa.x==bb.x)?aa.y<bb.y:aa.x<bb.x; } void dfs(int x){ //cout<<x<<' '; pos[x]=low[x]=++dfsclk; stk[++tp]=x; int p; for (p=fst[x]; p; p=nxt[p]){ int y=pnt[p]; if (!pos[y]){ dfs(y); low[x]=min(low[x],low[y]); } else if (!scc[y]) low[x]=min(low[x],pos[y]); } if (low[x]==pos[x]) for (scc[0]++; ; tp--){ sum[scc[0]]++; scc[stk[tp]]=scc[0]; if (stk[tp]==x){ tp--; break; } } } void solve(int x){ if (dp[x]) return; dp[x]=sum[x]; int p,tmp=0; for (p=fst[x]; p; p=nxt[p]){ solve(pnt[p]); tmp=max(tmp,dp[pnt[p]]); } dp[x]+=tmp; } int main(){ cnt=read(); int m=read(),n=read(),i,j,k; for (i=1; i<=cnt; i++){ a[i].x=read(); a[i].y=read(); a[i].z=read(); a[i].id=i; } sort(a+1,a+cnt+1,cmp_r); for (i=1; i<=cnt; i++) if (a[i].x!=a[i-1].x) k=(a[i].z==1)?i:0; else{ if (!k) continue; add(a[k].id,a[i].id); if (a[i].z==1) add(a[i].id,a[k].id); } int k0=1,k1=1,k2=1,last=0,now=2; for (i=a[1].x-1; i<=a[cnt].x; i++){ last=(last+1)%3; now=(now+1)%3; for (; a[k0].x<=i+1 && k0<=cnt; k0++) if (a[k0].x==i+1) val[a[k0].y][now]=a[k0].id; for (; a[k1].x<=i && k1<=cnt; k1++) if (a[k1].z==3 && a[k1].x==i) for (j=a[k1].y-1; j<=a[k1].y+1; j++) for (k=0; k<3; k++) if (val[j][k] && a[k1].id!=val[j][k]) add(a[k1].id,val[j][k]); for (; a[k2].x<=i-1 && k2<=cnt; k2++) if (a[k2].x==i-1) val[a[k2].y][last]=0; } sort(a+1,a+cnt+1,cmp_c); for (i=1; i<=cnt; i++) if (a[i].y!=a[i-1].y) k=(a[i].z==2)?i:0; else{ if (!k) continue; add(a[k].id,a[i].id); if (a[i].z==2) add(a[i].id,a[k].id); } for (i=1; i<=cnt; i++) if (!scc[i]) dfs(i); for (i=1; i<=cnt; i++) for (k=fst[i]; k; k=nxt[k]){ j=pnt[k]; if (scc[i]==scc[j]) continue; edg[++edg_p].x=scc[i]; edg[edg_p].y=scc[j]; } sort(edg+1,edg+edg_p+1,cmp); tot=0; memset(fst,0,sizeof(fst)); for (i=1; i<=edg_p; i++) if (edg[i].x!=edg[i-1].x || edg[i].y!=edg[i-1].y) add(edg[i].x,edg[i].y); int ans=0; for (i=1; i<=scc[0]; i++){ if (!dp[i]) solve(i); ans=max(ans,dp[i]); } printf("%d\n",ans); return 0; }
by lych
2016.2.9