[思维构造] 题解 Omkar and Duck

[思维构造] 题解 Omkar and Duck

题目链接

题目大意

构造一个 \(n\times n\) 的矩阵,每个元素取值范围为 \([0,10^{16}]\) ,使得所有从 \((1,1)\) 仅往下或往右走走到 \((n,n)\) 的路径的权值两两不同(一条路径的权值定义为这条路径经过的所有元素之和)。

\(1\le n\le 25\)

题目分析

思路一

神 @Imakf 提供了一种构造方法,把向右走认为是字符“a”,向左走认为是字符“b”,把所有 \((1,1)\)\((n,n)\) 的路径写成字符串然后按照字典序排序后依次钦定权值为 \(0,1,\dots,{2(n-1)\choose n-1}-1\) ,然后按照这个钦定的权值去给每个格子赋值就行了。

这样做为什么是对的?如果将所有路径插入 Trie 树,那么某个点 \((x,y)\)\((n,n)\) 的所有路径在 Trie 树上面是连续的,按照上面的构造方法所有路径的权值也就是连续的一段,由此不难发现这样构造就是正确的(或者说钦定的权值并不会出现冲突)。

具体如何实现?认为左上角是 \((1,1)\) 右下角是 \((n,n)\) ,给每个格子 \((x,y)\) 记录一个 \([L,R]\) 表示 \((x,y)\)\((n,n)\) 的所有路径的权值取值范围,先钦定第 \(n\) 列的所有元素取值 \(0\) ,然后按列数从大到小,行数从大到小确定每个格子元素的取值,先认为当前格子取值为 \(0\) ,看从当前格子向下走和向右走的权值是否有重复,如果有的话就加大当前格子下面格子的取值。这样构造在极限数据 \(n=25\) 时每个元素取值是符合范围的。

思路二

考虑二进制拆分,使用如下构造方式:

 0  0  0  0
 1  2  4  8
 0  0  0  0
 4  8 16 32

(奇数行所有元素为 \(0\) ,偶数行位置 \((x,y)\) 的值为 \(2^{x+y-3}\)

这种方法相当于把某个数二进制表示下若干连续的 \(1\) 绑定在一起,连续的 \(1\) 在同一行一起经过,不连续的 \(1\) 在不同行经过,不难发现这样构造也不会出现重复。

参考代码

思路一的代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;
template<typename T>void read(T&x){
	static char c;static int f;
	for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
	for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
	static char q[65];int cnt=0;
	if(x<0)pc('-'),x=-x;
	q[++cnt]=x%10,x/=10;
	while(x)
		q[++cnt]=x%10,x/=10;
	while(cnt)pc(q[cnt--]+'0');
}
long long L[35][35],R[35][35],val[35][35];
int main(){
	int n;read(n);
	for(int i=1;i<=n;++i){
		val[i][n]=0;
		L[i][n]=R[i][n]=0;
	}
	for(int j=n-1;j>=1;--j){
		val[n][j]=0;
		L[n][j]=R[n][j]=L[n][j+1];
		for(int i=n-1;i>=1;--i){
			val[i][j]=0;
			if(L[i+1][j]<=R[i][j+1]){
				long long delta=R[i][j+1]-L[i+1][j]+1;
				L[i+1][j]+=delta,R[i+1][j]+=delta;
				val[i+1][j]+=delta;
			}
			L[i][j]=L[i][j+1];R[i][j]=R[i+1][j];
		}
	}
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n;pc(" \n"[j==n]),++j)
			write(val[i][j]);
	fflush(stdout);
	int q;read(q);
	while(q--){
		long long x;read(x);
		int nx=1,ny=1;
		while(nx<=n&&ny<=n){
			write(nx),pc(' '),write(ny),pc('\n');
			if(nx<n&&L[nx+1][ny]<=x&&x<=R[nx+1][ny]){
				x-=val[nx+1][ny];++nx;
			}
			else{
				x-=val[nx][ny+1];++ny;
			}
		}
		fflush(stdout);
	}
	return 0;
}
/*
_|_|_|_|        _|_|      _|      _|   _|_|_|_|_|   _|_|_|_|_|   _|
_|      _|    _|    _|    _|_|    _|       _|       _|           _|
_|      _|   _|      _|   _| _|   _|       _|       _|           _|
_|      _|   _|_|_|_|_|   _|  _|  _|       _|       _|_|_|_|_|   _|
_|      _|   _|      _|   _|   _| _|       _|       _|           _|
_|      _|   _|      _|   _|    _|_|       _|       _|           _|
_|_|_|_|     _|      _|   _|      _|   _|_|_|_|_|   _|_|_|_|_|   _|_|_|_|_|

_|_|_|_|_|   _|      _|   _|_|_|_|     _|      _|
    _|        _|    _|    _|      _|    _|    _|
    _|         _|  _|     _|      _|     _|  _|
    _|          _|_|      _|      _|      _|_|
    _|         _|  _|     _|      _|       _|
    _|        _|    _|    _|      _|       _|
    _|       _|      _|   _|_|_|_|         _|

_|             _|_|_|     _|      _|   _|_|_|_|_|
_|           _|      _|   _|      _|   _|
_|           _|      _|    _|    _|    _|
_|           _|      _|    _|    _|    _|_|_|_|_|
_|           _|      _|     _|  _|     _|
_|           _|      _|      _|_|      _|
_|_|_|_|_|     _|_|_|         _|       _|_|_|_|_|

_|_|_|_|_|   _|      _|   _|_|_|_|_|
    _|        _|    _|        _|
    _|         _|  _|         _|
    _|          _|_|          _|
    _|           _|           _|
    _|           _|           _|
    _|           _|           _|

Created by xiaolilsq.
*/


posted @ 2021-03-30 21:50  xiaolilsq  阅读(167)  评论(0编辑  收藏  举报