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);
}
posted @ 2021-03-03 16:13  UCPRER  阅读(123)  评论(0编辑  收藏  举报