【YbtOJ#603】情报传递

题目

题目链接:https://www.ybtoj.com.cn/contest/120/problem/2

(n,m,tleq 10^5)

思路

UPD:卡过去了,把每次循环里邻接表去掉了,按照转移顺序扔进了一个数组里,这样内存连续寻址快。
这题卡常卡的就 NM 离谱。。。clock 过去的。
显然对于第一类要求,按照它的要求连边就是最优的,所以我们只需要判断按照这样的方法连边能不能满足第二类要求即可。
tarjan 把强连通分量缩一下,问题转化到这张 DAG 上。用 bitset 优化 DAG 传递闭包就可以做到时空 (O(frac{n^2}{omega})),可以得到 (60) 分。
考虑压缩空间,直接把点双分块,每次把一个块内的点双拿去跑拓扑,这样时间复杂度依然是 (O(frac{n^2}{omega})),空间只需要 (O(frac{nT}{omega})) 了。其中 (K) 是分的块的数量。
然后 tm 卡常卡死了。用 clock() 过了。

代码

#include <bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;

const int N=100010,lim=63;
int head[N],dfn[N],bel[N],low[N],U[N],V[N],X[N],Y[N];
int n,m,Q,tot,cnt;
bool vis[N];
ll s[N];
stack<int> st;

struct node
{
	int x,y;
}b[N];

bool cmp(node x,node y)
{
	return x.y<y.y;
}

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

void write(int x)
{
	if (x>9) write(x/10);
	putchar(x%10+48);
}

struct edge
{
	int next,to;
}e[N];

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void tarjan(int x)
{
	dfn[x]=low[x]=++tot;
	st.push(x); vis[x]=1;
	for (reg int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!dfn[v])
		{
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if (vis[v])
			low[x]=min(low[x],dfn[v]);
	}
	if (low[x]==dfn[x])
	{
		int y; cnt++;
		do {
			y=st.top(); st.pop();
			vis[y]=0; bel[y]=cnt;
		} while (y!=x);
	}
}

int main()
{
	freopen("gplt.in","r",stdin);
	freopen("gplt.out","w",stdout);
	memset(head,-1,sizeof(head));
	n=read(); m=read();
	for (reg int i=1;i<=m;i++)
	{
		U[i]=read(); V[i]=read();
		add(U[i],V[i]);
	}
	tot=0;
	for (reg int i=1;i<=n;i++)
		if (!dfn[i]) tarjan(i);
	memset(head,-1,sizeof(head)); tot=0;
	for (reg int i=1;i<=m;i++)
		if (bel[U[i]]!=bel[V[i]])
			add(bel[V[i]],bel[U[i]]);
	Q=read(); tot=0;
	for (reg int i=1;i<=Q;i++)
		b[i].x=bel[read()],b[i].y=bel[read()];
	sort(b+1,b+1+Q,cmp);
	for (reg int i=1;i<=cnt;i++)
		for (reg int j=head[i];~j;j=e[j].next)
			X[++tot]=e[j].to,Y[tot]=i;
	for (reg int k=1,l=1;(k-1)*lim<cnt;k++)
	{
		int L=(k-1)*lim+1,R=min(k*lim,cnt);
		for (reg int i=1;i<=cnt;i++)
			s[i]=(L<=i && R>=i)?(1LL<<(i-L)):0;
		for (reg int i=1;i<=tot;i++) s[X[i]]|=s[Y[i]];
		for (;l<=Q && b[l].y<=R;l++)
			if (s[b[l].x]&(1LL<<(b[l].y-L)))
				return printf("NO"),0;
	}
	printf("YES
%d
",m);
	for (reg int i=1;i<=m;i++)
		write(U[i]),putchar(32),write(V[i]),putchar(10);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14426273.html