把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF1479C】Continuous City(构造)

点此看题面

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

二进制分解

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

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

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

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

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

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

我们枚举\(i\in[2,21]\),如果\(R-L\)\(i-2\)位是\(1\),则令\(t\)表示将\(R-L\)\(i-2\)位都修改为\(0\)后的值,然后我们就从\(i\)\(n\)号点连一条权值为\(t+1\)的边。这样一来\(1\rightarrow 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\n",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\n",cnt),puts(s),0;//输出构造方案
}
posted @ 2021-02-08 10:43  TheLostWeak  阅读(328)  评论(0编辑  收藏  举报