2024牛客暑期多校训练营10 - L. Tada! - 题解
本文分离自《2024牛客暑期多校训练营10 - VP记录》。
L. Tada!
看到数据范围 \(1 \le N \le 5, 1 \le M \le 50\),一眼暴力判断/暴搜。
因为操作可逆,所以如果 \(x\) 能在 \(t\) 步内到达 \(y\),那么 \(y\) 也能在 \(t\) 步内通过同样的方式到达 \(x\)。
所以对于每一对 \(s,t\),我们只需要找到 \(s\) 能在 \(t\) 步内到达的点集就可以了。
首先,这里有两种浪费旋转次数的方式:
-
因为密码锁可以来回转浪费次数,而每一次这么做需要两步,所以如果能 \(s\) 在 \(x\) 步内达到 \(j\),那么 \((x+2)\) 步也可以。
-
只要长度 \(n\) 不等于 \(1\),那么就可以将一步拆成两步,例如:正向转 \([l,r]\) 可以转化成先正向转 \([l,r+1]\) 再反向转 \([r+1,r+1]\),效果相同。
通过以上两种方式,在 \(n>1\) 的条件下,如果 \(s\) 可以 \(x\) 步到 \(j\),那么我们就可以让它 \((x+k)\) 步到 \(j\)。换言之,如果可以执行以上两种操作且最少步数 \(x<t\) 时,一定合法。
考虑什么时候无法执行以上两种操作:
-
当 \(n=1\) 时,因为每次只能转 \(1\) 位,所以最少步数就是 \(\lvert s-j \rvert\),因为此时无法使用方法二(至少要两位),所以只能用第一种,也就必须保证 \(t - \lvert s-j \rvert\) 是偶数,可以通过两步两步地转转到终点。
-
当 \(s=j\) 时,最少步数将为 \(0\),此时我们没有步数可以拆,所以在最初无法使用方法二,只能使用方法一,而只要使用了一次方法一,就有步数可以拆了,问题转化为通常情况。但是,当 \(t=1\) 时,我们也不能使用第一种方式(至少要转两次)。两种方式都无法使用,又要求必须要转一次,当然不可行。
除此之外,一定能无限制使用这两种方法,即一定能增加确定的步数来让 \(s\) 到 \(j\)。
总结一下,以 \(s\) 为起点时,枚举所有可能的终点 \(j\),这个终点在以下三种情况下不合法:
- 从 \(s\) 到 \(j\) 所需最少步数大于 \(t\);
- \(n=1\) 时,\(s\) 到 \(j\) 的最少步数与 \(t\) 奇偶性不同;
- \(t=1\) 时,\(s\) 和 \(j\) 相同。
刚才,我们已经证明了只要以上三种不合法条件均不满足,那么就一定有方式可以用 \(t\) 步抵达 \(j\)。
由此可以判断出所有不合法的情况。当所有不合法的答案标记结束后,未被标记的一定是合法的答案,统计个数并分情况输出即可。
至于求最少步数可以用类似 DP 的递归处理,每次一定要把一位转到相同,计算下一位时判断是要和这一位一起转还是自己单独转,再分别对正着转和反着转取最小值。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=10,M=55,Pow10N=1e5+5;
const int pow10[N]={1,10,100,1000,10000,100000,1000000};
int n,m;
int ShortestPath(int x,int y,int alr=0,int pos=1) //x到y的最短距离,alr是操作上一位的次数,pos是已经处理的位数
{
if(pos>n) return 0;
//规定y转到x为正方向
int xe=x%10,ye=y%10;
int res=0x3f3f3f3f;
int nd=(xe-ye+10)%10; //把x和y的最低位转平所需的步数
//自己单转或跟着上一个一起转
res=min(res,max(0,min(nd,nd-alr))+ShortestPath(x/10,y/10,nd,pos+1)); //y转到x(与上一次同向)
nd=(ye-xe+10)%10;
res=min(res,max(0,min(nd,nd+alr))+ShortestPath(x/10,y/10,-nd,pos+1)); //x转到y(与上一次反向)
return res;
}
bool flag[Pow10N];
int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<=pow10[n];i++)
flag[i]=false;
for(int i=1;i<=m;i++)
{
int s,t; scanf("%d%d",&s,&t);
for(int j=0;j<pow10[n];j++)
{
if(flag[j]) continue;
if(ShortestPath(j,s)>t) flag[j]=true;
if(n==1 && (j-s+t)%2) flag[j]=true;
if(t==1 && s==j) flag[j]=true;
//三种不合法情况
}
}
int cnt=0,ans=0;
for(int i=0;i<pow10[n];i++)
if(!flag[i]) cnt++,ans=i;
if(cnt==0) printf("IMPOSSIBLE\n");
if(cnt==1) printf("%0*d\n",n,ans); //在前面补0
if(cnt>1) printf("MANY\n");
}
return 0;
}
本文采用 「CC-BY-NC 4.0」 创作共享协议,转载请注明作者及出处,禁止商业使用。
作者:Jerrycyx,原文链接:https://www.cnblogs.com/jerrycyx/p/18516899