2020小米网络赛第二场 F-Modulo Nine

Posted ZLTJohn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2020小米网络赛第二场 F-Modulo Nine相关的知识,希望对你有一定的参考价值。

题意

F-Modulo Nine
给定m个区间[li,ri],问带前导零的n位数,有多少个数满足: ∀ i , ∏ k = l i r i a k % 9 = = 0 \\forall i,\\prod_k=l_i^r_ia_k \\%9==0 i,k=liriak%9==0
其中a[k]表示这个数的第k位,n,m<=1000.

分析

思维方向有很多,比较靠谱的是逐位填数dp。
一个区间要合法,只需要有2个3的因子即可,一个0、9算两个3的因子,3、6算一个。我们先不考虑填0、9,考虑只填3、6。那么一个位置的状态只有两种,3的因子或者非3的因子。
我们只用记录最后两个3的因子填在哪里。因为假如一个区间有超过两个因子,前面的可以忽略掉。
设f[i][j]表示最后两个因子出现在i,j上(i<j),其中右端点在j之前的区间已经全部合法,的方案数。
这样的记录的好处是,当f[j][k]要从f[i][j]转移过来的时候,我们知道右端点在j之前的区间已经全部合法了,而j~k-1的区间还未判断,而且他们要合法的最后机会就是i足够大,使得区间包含i,而j一定包含。
那么 f [ j ] [ k ] = ∑ i f [ i ] [ j ] ∗ 6 k − j − 1 ∗ 2 ∗ [ 右 端 点 在 j 至 k − 1 的 区 间 的 左 端 点 都 小 于 等 于 i ] f[j][k]=\\sum_i f[i][j]*6^k-j-1*2*[右端点在j至k-1的区间的左端点都小于等于i] f[j][k]=if[i][j]6kj12[jk1i]
系数中,6的幂指j+1~k-1的位置可以填非3因子的数,2指的是k可以填3、6。
注意到限制条件中,左端点都<=i,就是最大值<=i。那么对于每个右端点相同的区间,我们保留左端点最大的那个即可,记Le[x]表示右段点在x的最大的左端点下标。
现在的还有0\\9没有考虑。事实上,当我们得到了f[j][k],我们可以直接用相同的方法转移到f[k][k],只是系数变成了1,因为之前填3\\6时,这个位置算了两种方案,0\\9刚好也是两个。
初始状态就是f[0][0]=1,最后的答案可以用所有f[j][k]中符合要求的状态求得。
这样暴力转移是O(n^3)的,注意到转移中合法的i一定是连续的,拿个前缀和优化一下转移就行了。

代码

#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int N=1e3+5,mo=1e9+7,M=1e7+5;
ll f[N][N],six[N],coef,ans,g[N][N];
int Le[N],Lmx,n,m,l,r,i,j,k;
ll get(int l,int r,int j)

	if (!l) return g[r][j];
	else return g[r][j]-g[l-1][j];

int main()

	freopen("F.in","r",stdin);
	//freopen("I.out","w",stdout);
	n=1e3;
	six[0]=1;
	fo(i,1,n) six[i]=six[i-1]*6ll%mo;
	while (scanf("%d %d",&n,&m)!=EOF)
	
		ans=0;
		fo(i,1,n) Le[i]=0;
		fo(i,0,n) fo(j,0,n) g[i][j]=f[i][j]=0;
		fo(i,1,m) 
		
			scanf("%d %d",&l,&r);
			Le[r]=max(Le[r],l);
		
		f[0][0]=1;
		g[0][0]=1;
		fo(k,1,n)
		
			Lmx=0;
			fd(j,k-1,0)
			
				// f[j][k]: f[Lmx[j~k-1]~j][j]
				Lmx=max(Lmx,Le[j]);
				if (Lmx>j) break;
				f[j][k]=get(Lmx,j,j)*six[k-j-1]*2%mo;
			
			g[0][k]=f[0][k];
			fo(j,1,k-1) g[j][k]=(g[j-1][k]+f[j][k])%mo;
			f[k][k]=g[k-1][k];
			g[k][k]=(g[k-1][k]+f[k][k])%mo;
		
		Lmx=0;
		fo(i,1,n) Lmx=max(Lmx,Le[i]);
		fo(i,0,n)
			if (Lmx<=i)
				fo(j,i,n)
				
					ans=(ans+f[i][j]*six[n-j])%mo;
				
		printf("%lld\\n",(ans+mo)%mo);
	

以上是关于2020小米网络赛第二场 F-Modulo Nine的主要内容,如果未能解决你的问题,请参考以下文章

《蓝桥杯真题》:2020年单片机省赛第二场(第十一届第二场)

2020第十一届蓝桥杯真题JAVA B组省赛第二场答案分享(2020.10.17)

《蓝桥杯真题》:2020年单片机省赛第二场

ICPC网络赛第二场G Limit

ICPC网络赛第二场G Limit

icpc网络赛第二场K Meal