CF1137C Museums Tour

CF1137C Museums Tour

题目传送门
思路:
带阴间题
这题关键在于想到建分层图。
我们设第 i i i 层的 n n n 个点表示星期 i i i n n n 个博物馆的 0 / 1 0/1 0/1 状态。
建图的时候对于一条边 ( u , v ) (u,v) (u,v),我们从第 i i i 天的 u u u 连向第 i + 1 i+1 i+1 天的 v v v
然后对于这个图进行缩点(这还挺显然的 ),再对于这个 D A G DAG DAG 跑一个最长链就好啦!
但有一些细节,怎么判断同一个 u u u i , j i,j i,j 两天有没有重复计算呢?
对于 u i u_i ui u j u_j uj 在同一个强连通分量的情况,我们只需要记录一下已经产生贡献的点就不会重复了
但是对于 u i u_i ui u j u_j uj 不在同一强连通分量的情况就有点麻烦。
如果 u i u_i ui 能到达 u j u_j uj,那么说明 u u u 这个点经过 j − i j-i ji 天能走回自己,所以走 d − 1 d-1 d1 次就能就能走回 u i u_i ui 这个点,说明 u j u_j uj 能到达 u i u_i ui,即 u i u_i ui u j u_j uj 能相互到达,这与 u i u_i ui u j u_j uj 不在同一强连通分量相悖,所以这种情况不存在。
然后我们就切掉了这道紫题QwQ
代码:

#include<bits/stdc++.h>
#define freo(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout)
#define ll long long
#define id(i,j) ((i-1)*d+j)
using namespace std;
const int maxn=1e7+5;
int n,m,d,head[maxn],ecnt,low[maxn],st[maxn],top,dfn[maxn];
int cnt,tot,col[maxn],buc[maxn],vis[maxn];
struct edge
{
	int nxt,to;
}e[maxn];
inline ll read()
{
	ll ret=0;char ch=' ',c=getchar();
	while(!(c<='9'&&c>='0')) ch=c,c=getchar();
	while(c<='9'&&c>='0') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
	return ch=='-'?-ret:ret;
}
inline void add(int x,int y)
{
	e[++ecnt]=(edge){head[x],y};
	head[x]=ecnt;
}
inline void tarjan(int u)
{
	dfn[u]=low[u]=++cnt;st[++top]=u;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
		else if(!col[v]) low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u])
	{
		col[u]=++tot;
		if(vis[u]&&buc[(u+d-1)/d]!=tot) buc[(u+d-1)/d]=tot,low[tot]++;
		while(st[top]!=u)
		{
			int now=st[top--];
			col[now]=tot;
			if(vis[now]&&buc[(now+d-1)/d]!=tot) buc[(now+d-1)/d]=tot,low[tot]++;
		}
		top--;
	}
}
inline int dfs(int u)
{
	if(~dfn[u]) return dfn[u];
	dfn[u]=low[u];
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		dfn[u]=max(dfn[u],low[u]+dfs(v));
	}
	return dfn[u];
}
int main()
{
	n=read(),m=read(),d=read();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		for(int j=1;j<=d;j++) add(id(u,j),id(v,j%d+1));
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=d;j++)
		{
			int x=id(i,j);
			scanf("%1d",&vis[x]);
		}
	}
	tot=id(n,d);
	for(int i=1;i<=id(n,d);i++) if(!dfn[i]) tarjan(i);
	for(int u=1;u<=id(n,d);u++)
	{
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(col[u]!=col[v]) add(col[u],col[v]);
		}
	}
	memset(dfn,-1,sizeof(dfn));
	printf("%d\n",dfs(col[id(1,1)]));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值