【Moscow Workshop 2019 WinterCamp day3 divB L】LED-led Paths 题解

题目大意

       ~~~~~~       给定一幅 n n n 个点 m m m 条边的 DAG。你有三种颜色(红蓝绿),你要给每条边染色,使得不存在一条路径上连续 42 条边同色。
       n ≤ 50000 ,   m ≤ 2 e 5 ~~~~~~n \leq 50000,~m \leq 2e5       n50000, m2e5

\\
\\
\\

naive想法1

       ~~~~~~       今天的主题是 random algorithm,于是下午的 contest 就变成了随机大战+乱搞大战。于是这题理所当然地首先想想随机。
       ~~~~~~       似乎直接给每条边随机一个颜色很优秀啊?你想想,一条路径连续 42 个同色的概率不是几乎为 0 吗?
       ~~~~~~       然而非常好叉,因为这不是随机图,只要让两个点之间重边非常多,那么在随机下每个点到另一个点什么颜色都有了,轻松 42 条同色。
       ~~~~~~       然后又脑洞了一下:给每个点随机一种颜色然后这个点的出边全都是这种颜色、按 bfs 序每15层全部同色轮换然后其他随机……全都 WA 了。然后发现,只要把点分层,每层 100 个,共 500 层,相邻两层之间完全相连,不同层之间再随便加些边扰动一下,那么在随机下每层到下一层也是什么颜色都有,依然轻松 42 条同色。

       ~~~~~~       因此本质问题是,上面这种数据就是卡掉所有随机的。思考的方向不是让它如何更随机,因为越随机,这种数据越过不去。应该构造一种方案使得它在这种情况下不那么随机。
       ~~~~~~       因此这是一个构造题。

naive想法2

       ~~~~~~       需要构造一种方法,使得如果连续 42 条边同色的话,会有什么东西爆掉。比如让点的编号爆掉。
       ~~~~~~       有一种很 naive 的想法是,设 x x x 的二进制最高位的 1 是从右往左数第 w x w_x wx 位,比如 i i i 连向 j j j,若 i < j i<j i<j,且 w i < w j w_i<w_j wi<wj,那么 i i i j j j 连红边。这样的话,红边连续意味着二进制最高位的 1 一直向前推进,那么连续 42 次编号肯定爆了。然后比如当 w i > w j w_i>w_j wi>wj 时连绿边,那么连续 42 条绿边也爆了……

       ~~~~~~       但是还有很多情况没考虑,比如 w i = w j w_i=w_j wi=wj 怎么连……会发现三种颜色不太够,因此方法要改进一下。

       ~~~~~~       基于这种思路就得出了一种构造方法。
       ~~~~~~       首先为了去掉 i > j i>j i>j 的情况,我们用拓扑序编号来做。对于 i i i 连向 j j j,设它们的拓扑序编号分别为 i d i id_i idi i d j id_j idj,那么一定有 i d i < i d j id_i<id_j idi<idj
       ~~~~~~       然后我们不要讨论最高位的 1,而是讨论 i d i id_i idi i d j id_j idj 二进制下第一个不同的位,即 w i d i ⊕ i d j w_{id_i \oplus id_j} widiidj。由于 n ≤ 50000 n \leq 50000 n50000,因此 i d id id 二进制最多只有 16 位,那么我们对 w i d i ⊕ i d j w_{id_i \oplus id_j} widiidj 进行讨论:若为 1 1 1~ 5 5 5,连红边;若为 6 6 6~ 10 10 10,连绿边;若为 11 11 11~ 16 16 16,连蓝边。这样红边和绿边的连续转移都不超过 2 5 = 32 2^5=32 25=32 次。蓝边因为有第 16 位,严格分析一下最大是 48 48 48 次。

       ~~~~~~       比赛时脑子抽了以为蓝边也是 32 次,于是勇敢地写了,然后过了。。。
       ~~~~~~       估计出题人也没想过我这种偏门的想法于是没有专门出数据去卡。。。
       ~~~~~~       直到写这个 blog 重新证明一次才发现被叉掉了 qaq
       ~~~~~~       不过这场本身也是乱搞大战,我乱搞过了这个题不也算是正解吗(大雾

       ~~~~~~       (当然还是希望有 dalao 继续完善一下这个想法。

题解

       ~~~~~~       然后题解异常地简单,思路很直接。(其实是因为我想的太复杂了qaq

       ~~~~~~       先对点进行分组,分成 42 42 42 个组,每组 n 42 \frac{n}{42} 42n 个点。组与组之间的转移连红边。由于是 DAG,红边连续最多 41 次。
       ~~~~~~       对于每组内,再分 42 42 42 个小组,每小组 n 42 ∗ 42 \frac{n}{42*42} 4242n 个点。小组与小组之间转移连蓝边。同理,蓝边连续最多 41 次。
       ~~~~~~       由于小组内点数 n 42 ∗ 42 < 42 \frac{n}{42*42}<42 4242n<42,因此小组内转移连绿边。这样绿边连续也不超过 42 次了。

       ~~~~~~       这个方法用的思想是 4 2 3 > n 42^3>n 423>n,因此连续分组两次就行了。

代码

// naive想法2

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int maxn=5e4+5, maxm=2e5+5;

int n,m;
char clr[3]={'R','B','G'};

int tot,go[maxm],fro[maxm],nxt[maxm],f1[maxn],com[maxn];
void ins(int x,int y)
{
	go[++tot]=y;
	fro[tot]=x;
	nxt[tot]=f1[x];
	f1[x]=tot;
	com[y]++;
}

int d[maxn],dis[maxn];
void topo()
{
	int j=0;
	fo(i,1,n) if (!com[i])
	{
		d[++j]=i;
		dis[i]=1;
	}
	for(int i=1; i<=j; i++)
	{
		for(int p=f1[d[i]]; p; p=nxt[p])
		{
			dis[go[p]]=max(dis[go[p]],dis[d[i]]+1);
			if (--com[go[p]]==0) d[++j]=go[p];
		}
	}
}

int main()
{
	srand(time(0));
	
	scanf("%d %d",&n,&m);
	fo(i,1,m)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		ins(x,y);
	}
	
	topo();
	
	fo(i,1,m)
	{
		int w=0;
		for(int x=dis[fro[i]]^dis[go[i]]; x; x>>=1, w++);
		printf("%c\n",clr[w%3]);
	}
}

// 官方题解思路很简单,懒得再写了 qaq

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值