题意:
给定一个有n个顶点的有向图,任何一个顶点u与其他任意一个顶点v间都有两条有向边(u,v)(v,u),找到一条按字典序排列的走完所有边的序列,输出序列里从L到R的序列。
题解:
贪心就可以了,每次选择可以走的路线中,字典序最小的顶点去走。下面以5个顶点为例说明。
第一步一定是顶点1,下面贪心就是顶点2,然后回到顶点1,顶点2已经走过了,走顶点3,以此类推,可以得到序列 1 2 1 3 1 4 1 5,此时与顶点1有关的边只有(5,1),这条边是最后走的。所以走顶点2,最后可以得到完整序列:1 2 1 3 1 4 1 5 2 3 2 4 2 5 3 4 3 5 4 5 1
观察可以发现,我们可以把序列排成下面的情形:
1 2 1 3 1 4 1 5
2 3 2 4 2 5
3 4 3 5
4 5
1
一共5层,这样可以在O(n)的时间里找到位于L位置的顶点,以此为起点输出序列直到位置R。
代码如下:
#include <bits/stdc++.h>
using namespace std;
long long t,n,l,r;//这里用long long 是怕乘法计算溢出(会不会溢出我也不知道(笑哭))
int main ()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>t;
while (t--)
{
cin>>n>>l>>r;
if (l==(n*(n-1)+1))//位置为序列末端直接输出1就好了
{
cout<<'1'<<endl;
continue;
}
long long sum=0,w_place=l;//sum为位置的前缀和,w_place为当前位置
long long c_place;//记录层数位置
for (int i=1;i<n;i++)//找到L位置的层数与在当前层数的位置
{
sum+=(n-i)*2;
if (l<=sum)
{
c_place=i;
break;
}
w_place-=(n-i)*2;
}
long long answer;
for (int i=1;i<(r-l+2);i++)//输出序列
{
if (w_place%2)//不难发现奇数位置输出的顶点就是层数
{
if (c_place==n)//最后一层是例外
cout<<'1'<<' ';
else
cout<<c_place<<' ';
w_place++;
}
else
{
answer=w_place/2+c_place;//偶数位置顶点则满足该公式
cout<<answer<<' ';
if (answer==n)//顶点与n相等进入下一层
{
c_place++;
w_place=1;
}
else//不等的话位置+1就好了
{
w_place++;
}
}
}
cout<<endl;
}
return 0;
}