【CF1479C】Continuous City(构造)

点此看题面

  • 给定(L,R),你可以用至多(32)个点构造一张简单(DAG),并给每条边定一个正边权。
  • 要求使得(1 ightarrow n)所有路径长度构成的集合恰好是([L,R])
  • (1le Lle Rle10^6)

二进制分解

这种题目应该很容易往二进制分解的方向去想。

首先说明,无论(L,R)取何值,答案都一定是(YES),且按照我的方法应该只要让(n=22)就可以了。

考虑我们先从(1)号点向所有点连一条长度为(L)的边,现在问题就在于如何构造出值域为([1,R-L])的路径长度。

对于所有(iin[2,20]),从(i)号点向之后除(n)号点以外所有点连一条长度(2^{i-2})的边。

容易发现,此时(1 ightarrow i)的路径长度值域其实是([L,L+2^{i-2}-1])

接下来就是要考虑怎么用它们来凑出(R-L),实际上我们可以直接让(23)号点成为(n)号点。

我们枚举(iin[2,21]),如果(R-L)(i-2)位是(1),则令(t)表示将(R-L)(i-2)位都修改为(0)后的值,然后我们就从(i)(n)号点连一条权值为(t+1)的边。这样一来(1 ightarrow n)路径长度的值域就添上了([L+t+1,L+t+2^{i-2}])

具体的理解最好还是自己画个图比较好。

代码:(O(20^2))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 32
#define Add(x,y,z) (tot+=sprintf(s+tot,"%d %d %d
",x,y,z),++cnt)//存下一条边的输出,因为要先输出边数
using namespace std;
int L,R,cnt,tot;char s[10000000];
int main()
{
	RI i,j,t;scanf("%d%d",&L,&R),puts("YES"),Add(1,22,L);//从1号点向n号点连边
	for(i=1;i<=20;++i) for(t=i^1?1<<i-2:L,j=i+1;j<=21;++j) Add(i,j,t);//从1~20号点向之后除n号点以外的点连边
	for(t=R-L,i=2;i<=21;++i) t>>(i-2)&1&&(t^=1<<i-2,Add(i,22,t+1));//将R-L二进制分解
	End:return printf("22 %d
",cnt),puts(s),0;//输出构造方案
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1479C.html