ZROI 1254 迷宫(交互)

交互题真难调…

题目描述

小 D 正在走迷宫。
这个迷宫可以被看做 n n n 个点, m m m 条边的连通无向图。
小 D 在每个点时,仅能够看到这个点出发有多少条路。自然地,小 D 可以知道他是从第几条路过来的。显然,仅有这些信息是不够的。因此,小 D 决定给节点做一些标记:
小 D 共可以做 k k k 种标记,每个节点同时只能是一种标记。初始时,所有节点都是第一种标记(即没有标记)。
小 D 希望移动不超过 L 步就还原出迷宫的结构:他想要知道,对于每个 1 ≤ d ≤ n 1 ≤ d ≤ n 1dn,图中共有多少对节点 ( u , v ) (u, v) (u,v) 满足节点 u u u, v v v 的最短路恰好为 d d d 呢?
但他并不会,请你帮帮他。
你在迷宫中行走的步数不超过 L L L
1 ≤ n ≤ 200 , n − 1 ≤ m ≤ n ( n − 1 ) 2 , 3 ≤ k , 14 m ≤ L 1\leq n \leq 200,n-1\leq m\leq \frac{n(n-1)}{2},3\leq k,14m\leq L 1n200,n1m2n(n1),3k,14mL
ZROI 1254 迷宫(交互)_第1张图片
ZROI 1254 迷宫(交互)_第2张图片

题解

要求的答案只是一种可行的加密这个图的方式而已,我们只需要求出这个图然后dfs一遍即可。
首先如何dfs?
发现题目给出的操作非常支持dfs,具体来说,我们定义三种标记1,2,31是初始标记,2表示访问过且当前点在dfs栈中,3则表示访问过但当前点不在dfs栈中。
在dfs中如果我们接下来走到标记为3的点我们能够肯定这条边是被访问过的(否则应该之前就访问过该点,因此忽略),走到标记2则表示我们走到一条返祖边,记录一下。
这一遍dfs我们会move 4 m − 2 n 4m-2n 4m2n次,每一条非树边会正反分别走两遍。
现在我们得到了原图的一个生成树,然后我们现在还原返祖边。
注意到我们只有 3 3 3个标记,而我们需要还原返祖边的具体编号,我们可以将编号用三进制的方法还原,具体来说我们第 k k k次还原这个编号的三进制下第 k k k位的数,我们只需要将每个点的标记设为三进制第 k k k位的数+1即可。
这样的move次数是 2 ⌊ log ⁡ 3 n ⌋ m ≤ 10 m 2\lfloor \log_3 n\rfloor m\leq 10m 2log3nm10m的。
因此总的move次数 ≤ 14 m \leq 14m 14m,满足要求。
当然这个实现返祖边和树边是一起考虑的:)
时间复杂度: O ( ( n + m ) log ⁡ 3 n ) O((n+m)\log_3 n) O((n+m)log3n)

细节

这可能是我…第一次做OI赛制的交互题???然后整出了一堆问题…

  • 在本地编译程序需要用到命令g++ -o maze maze.cpp grader.cpp -std=c++11 -O2 -lm,我们首先需要将g++加入环境变量中才能顺利执行。
  • 本题的交互库没有良心的用了c++11,如果你的g++版本过低当然无法编译。于是我们替换一下交互库里的auto就好了。
  • 如果你运行exe时发生了读入之后瞬间消失的现象,建议加上system("pause");(可能是因为g++版本太老的原因)
  • 如果你实在受不了交互库,那当然可以自己当交互库:)具体可以参考下面注释里的东西。小心累死

实现

#include "maze.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;

#define LL long long
#define DB double
#define MAXN 400
#define MOD 998244353
#define Pr pair
#define X first
#define Y second
#define INF 100000
#define mem(x,v) memset(x,v,sizeof(x))

LL read(){
	LL x=0,F=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	return x*F;
}

// You can call the following functions:
/*int get_edge_number(){
	printf("get_edge_number()\n");
	return read();
}
int get_coming_edge(){
	printf("get_coming_edge()\n");
	return read();
}
int get_label(){
	printf("get_label()\n");
	return read();
}
void set_label(int x){
	printf("set_label(%d)\n",x);
}
void move(int e){
	printf("move(%d)\n",e);
}*/

int n,du[MAXN+5];
int T[MAXN+5][MAXN+5],G[MAXN+5][MAXN+5],dis[MAXN+5][MAXN+5];
int num[MAXN+5],a[MAXN+5],b[MAXN+5],pw;

int dfs1(){
	int x=++n;
	int fa=get_coming_edge();
	du[x]=get_edge_number();
	set_label(2);
	for(int i=1;i<=du[x];i++)T[x][i]=G[x][i]=0;
	for(int i=1;i<=du[x];i++){
		move(i);
		int col=get_label();
		if(col==1)T[x][i]=dfs1();
		else if(col==2)T[x][i]=-1,move(get_coming_edge());
		else T[x][i]=-2,move(get_coming_edge());
	}
	set_label(3);
	if(x!=1)move(fa);
	return x;
}
void dfs2(int x){
	int fa=get_coming_edge();
	set_label(b[x]);
	for(int i=1;i<=du[x];i++){
		if(T[x][i]==-1){
			if(i==fa)continue;
			move(i);
			G[x][i]+=pw*(get_label()-1);
			move(get_coming_edge());
		}
		else if(T[x][i]>0){
			move(i);
			G[T[x][i]][get_coming_edge()]+=pw*(b[x]-1);
			dfs2(T[x][i]);
		}
	}
	if(x!=1)move(fa);
}
std::vector<int> calc(){
	std::vector<int> answer(n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		dis[i][j]=(i==j)?0:INF;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=du[i];j++)
		if(T[i][j]==-1)dis[i][G[i][j]+1]=dis[G[i][j]+1][i]=1;
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
	for(int i=1;i<=n;i++)
		for(int j=i+1;j<=n;j++)
		answer[dis[i][j]-1]++;
	return answer;
}

std::vector<int> solve(int k, int lim)
{
	int rt=dfs1();
	for(int i=1;i<=n;i++)a[i]=i-1;
	pw=1;
	while(pw<n){
		for(int i=1;i<=n;i++)
		b[i]=a[i]%3+1,a[i]/=3;
		dfs2(rt);
		pw*=3;
	}
	return calc();
}

/*int main(){
	std::vector res=solve(3,100);
	for(int i=0;i

你可能感兴趣的:(ZROI 1254 迷宫(交互))