2018 ICPC Asia Jakarta Regional Contest C. Smart Thief (欧拉路径+哈希)
题意:
给定m个数字(m<=9),要求用这些数字构造出一个最短的串,使得它有k不同的个长度为n的连续子串。
思路:
两个相邻的子串,前一个串的后n-1位就是后一个子串的前n-1位,因此想到可以将这个构造问题转化为图上的问题。
图上的点表示一个子串的后n-1位,边表示在后面插入一个新的数字(1~m),每个点都有m条边指向它能转化成的其他状态。一个点加上一条出边就代表了一个长度为n的子串,显然只要在图上走一条不经过重复边的路径,长度为k,就能得到答案。观察这个图可以发现每个节点都有m个出度和m个入度,因此它必定具有欧拉回路,所以答案要求的最短的串长度就是n+k-1,因为其k个子串都是不相同的。
显然可以用Hierholzer算法找欧拉路,但是图上的点可能有很多,如果dfs的过程中深度超过了k或者答案栈中的元素个数超过了k就可以直接跳出输出答案(答案栈中的序列一定构成一条不经过重复边的路径)。
因为节点的值很大,采用哈希的方式来判断一个点边是否被访问过。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const ll mod=1e9+7;
ll seed=2333;
ll base[maxn];
int dig[15];
int st1[maxn],st2[maxn],top1=0,top2=0;//top1:递归深度,top2:答案栈大小,st1:当前递归序列,st2:答案栈
int n,m,k;
map<ll,int>vis;
void print1(){
for(int i=1;i<=n-1;i++)printf("%d",dig[0]);
for(int i=1;i<=k;i++)printf("%d",dig[st1[i]]);
}
void print2(){
for(int i=1;i<=n-1;i++)printf("%d",dig[0]);
for(int i=1;i<=k;i++)printf("%d",dig[st2[top2--]]);
}
void dfs(ll x){//x:当前节点(数的后n-1位)
if(top1>=k){//递归深度到k
print1();exit(0);
}
for(int i=0;i<m;i++){//枚举边
ll cur=(x*seed%mod+i)%mod;//当前数字
if(!vis.count(cur)){
vis[cur]=1;
st1[++top1]=i;
ll v;
if(top1<n){//序列长度小于n
v=cur;
}
else{//序列长度达到n
v=cur-st1[top1-n+1]*base[n]%mod;
if(v<0)v+=mod;
}
dfs(v);
top1--;//回溯
st2[++top2]=i;
if(top2>=k){//遍历到k个数字
print2();exit(0);
}
}
}
}
int main () {
base[1]=1;
for(int i=2;i<=maxn-5;i++)base[i]=base[i-1]*seed%mod;
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<m;i++)scanf("%d",&dig[i]);
dfs(0);
}