原文链接https://www.cnblogs.com/zhouzhendong/p/ARC102D.html
题目传送门 - ARC102D
题意
给定 $L$,请你构造一个节点个数为 $n$ ,边数为 $m$ 的图,边带权,满足以下条件:
1. $nleq 20$
2. $mleq 60$
3. 如果有向边 $a ightarrow b$ 存在,那么 $a<b$ 。
4. 从 $1$ 走到 $n$ 总共有 $L$ 种不同的路径,这 $L$ 条路径的长度分别为 $0,1,cdots , L-1$ 。
$Lleq 10^6$
题解
垃圾翻译告诉我 $ngeq 20$ 。于是我立马构造了一个 $40$ 个点的图来满足。在看样例的时候,我发现读错了题目。
然后我就一直在想如何用 $2^k$ 的边权来构造。不知道为什么我只在想用这种边权构造。
然后我扔掉这种做法想出了一个 AC 做法,5分钟敲完 AC 了。赛后,Funtionendless 给我讲了一下他口胡的做法,然而我发现和我之前想的假做法好像,说他是错的;然后最后我发现我…… 于是我又知道了一种做法。
由于这两种做法的正确性都比较显然,所以不加解释。
做法1:by me
build(x,L){//以x为当前子图的最小标号节点,构造一个具有 [0,L] 的路径长度的图 if (L==0){ AddEdge(x,n,0); return; } if (L==1){ AddEdge(x,n,0); AddEdge(x,n,1); return; } y=NewNode(); if (L mod 2==0) AddEdge(x,n,L); if (L mod 2==0) L=L div 2-1; else L=L div 2; AddEdge(x,y,0); AddEdge(x,y,L); build(y,L); }
做法2:by Funtionendless
int calc(int x,int i){//以x为当前子图的最小标号节点,构造一个具有 [0,L) 的路径长度的图 return x&~((1<<(i+1))-1); } int GetD(int x,int i){ return x的i次二进制位; } n=20; build(x,L){ for (i = 0 to 18){ AddEdge(i+1,i+2,Pow(2,i)); AddEdge(i+1,i+2,0); } if (GetD(0)) AddEdge(1,n,calc(L,0)); for (i = 1 to 19) if (GetD(L,i)==1) AddEdge(i,n,calc(L,i)); }
代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=105; LL read(){ LL x=0,f=1; char ch=getchar(); while (!isdigit(ch)&&ch!='-') ch=getchar(); if (ch=='-') f=-1,ch=getchar(); while (isdigit(ch)) x=(x<<1)+(x<<3)+ch-48,ch=getchar(); return x*f; } int n,m=0,L; int A[N],B[N],C[N]; void push(int a,int b,int c){ m++; A[m]=a; B[m]=b; C[m]=c; } int main(){ L=read()-1; n=20; int cnt=1; while (L>=0){ if (L==0){ if (cnt<n) push(cnt,n,0); break; } if (L==1){ push(cnt,n,0); push(cnt,n,1); break; } if (L%2==0) push(cnt,n,L); L=(L+1)/2; push(cnt,cnt+1,0); push(cnt,cnt+1,L); L--; cnt++; } printf("%d %d ",n,m); for (int i=1;i<=m;i++) printf("%d %d %d ",A[i],B[i],C[i]); return 0; }