bzoj1924 所驼门王的宝藏 有向图最长链

    思路还是简单的,代码就。。。。反正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

你可能感兴趣的:(dp,图论,DFS,强连通分量,最长路)