【2018icpc Regional Jakarta C】Smart Thief 题解

题目大意

  给出 M M M 个个位数。现在你要用它们构造一个最短的数字串,使得这个串所有长度为 N N N 的连续子串,至少有 K K K 种。
  保证存在长度在 1e5 以内的答案。
   N ≤ 1 0 5 ,   M ≤ 10 ,   K ≤ min ⁡ ( M N , 1 0 5 ) N\leq10^5,~M\leq10,~K\leq \min(M^N,10^5) N105, M10, Kmin(MN,105)

“这是我初二时的 GDOI 题。”
——1队dalao

\\
\\
\\

题解

  要想到,一个子串后面添加一个数字成为下一个子串,这个类似于点通过边来转移,因此用欧拉路径来建图。

  考虑当 N N N 很小的时候。
  把所有长度为 N − 1 N-1 N1 的串视为点,表示子串的最后 N − 1 N-1 N1 位。共有 M N − 1 M^{N-1} MN1 个点。那么点与点之间就可以连转移边了,连边表示状态转移,比如 123 123 123 连一条权值为 4 4 4 的边到 234 234 234 去。
  对这个图跑一次欧拉路径,那么每个点加上它连出去的边,就是题目要求的一个子串,而且都是不同的,因此总的串就是最短的。

   N N N 很大的时候呢,
  找到最小的 N ′ N' N,使得 M N ′ ≥ K M^{N'}\geq K MNK。那么对于所有长度为 N N N 的子串,只看最后 N ′ N' N 位也足够满足 K K K 的要求了,换句话说前 N − N ′ N-N' NN 位是无关紧要的。因此把 N N N 换成 N ′ N' N 按上面的做法做就行了。

代码

// 这个代码在 opentrain 上能过,在 cf 上会神奇地 WA 样例,似乎是搞爆了它的 spj ??

#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

typedef long long LL;

const int maxtot=1e6+5, maxnp=30, maxn=1e5+5;
const LL mo=1e9+7, px=998244353;

int n,m,k,np,a[15];
LL mm,powpx[maxnp];

unordered_map<LL,int> M;
int tot;
vector<pair<int,int>> e[maxtot];
void dfs1(int k,LL now)
{
	if (k==np)
	{
		M[now]=++tot;
		return;
	}
	fo(i,1,m) dfs1(k+1,(now*px+a[i])%mo);
}
void dfs2(int k,LL now,int fir)
{
	if (k==np)
	{
		int my=M[now];
		fo(i,1,m)
		{
			int go=M[((now*px+a[i]-fir*powpx[np-1])%mo+mo)%mo];
			e[my].push_back(make_pair(go,a[i]));
		}
		return;
	}
	fo(i,1,m) dfs2(k+1,(now*px+a[i])%mo,(fir==-1)?a[i]:fir);
}

int nowh[maxn],d0,st,d[maxn];
void Euler(int k,int lim)
{
	int sz=e[k].size();
	for(int i=nowh[k]; i<sz; i=nowh[k])
	{
		nowh[k]=i+1;
		Euler(e[k][i].first,lim);
		if (d0==lim) return;
		d[++d0]=e[k][i].second;
		st=k;
		if (d0==lim) return;
	}
}

int rc[maxn];
bool pd;
void dfs3(int k,LL now)
{
	if (k==np)
	{
		if (M[now]==st)
		{
			fo(i,1,np-1) printf("%d",rc[i]);
			pd=1;
		}
		return;
	}
	fo(i,1,m)
	{
		rc[k]=a[i];
		dfs3(k+1,(now*px+a[i])%mo);
		if (pd) return;
	}
}

int main()
{
	scanf("%d %d %d",&n,&m,&k);
	fo(i,1,m) scanf("%d",&a[i]);
	
	for(np=1, mm=m; mm<k; np++, mm*=m);
	
	powpx[0]=1;
	fo(i,1,np) powpx[i]=powpx[i-1]*px%mo;
	dfs1(1,0);
	dfs2(1,0,-1);
	
	Euler(1,k);
	
	fo(i,1,n-np) printf("%d",a[1]);
	dfs3(1,0);
	fd(i,d0,1) printf("%d",d[i]);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值