zoukankan      html  css  js  c++  java
  • 7375. 【2021.11.11NOIP提高组联考】叁仟柒佰万

    Description

    给定一个序列 (a) ,你需要把它划分成任意多段,满足任意一段的 (mex) 值相同,求方案数,对 (10^{9}+7) 取模。

    定义一个区间的 (mex) 为区间中最小的没有出现过的自然数。

    (1le nle 37000000)

    Solution

    首先有个结论,就是最后划分的各个区间的 (mex) 跟整个区间的 (mex) 是一样的。

    证明:

    令全局 (mex)(K), 则说明 (0 sim K-1) 的数都存在于数列中而 (K) 不存在。

    假设我们最后每个区间的 (mex) 均为 (X)

    (X<K), 由于序列中为 (X) 的数存在, (X) 必定在其中一个区间中, 与所有区间 (mex=X) 矛盾。

    (X>K), 由于不存在 (K), 显然不合法。

    于是只能是 (X=K)

    知道了这个结论,( ext{dp}) 转移式子就好搞了。

    (f_i) 表示到 (i) 结尾的方案数,转移方程:

    (f_i=sum_{1le j<i} f_j[get(j,i)=mex])(令 (get(j,i)) 表示区间 (jsim i)(mex))。

    注意到 (get(j,i)) 在某个 (j) 使得 (get(j,i)=mex) 后,保持不变。

    因此我们只需要在 (mathcal{O}(n)) 的时间里求出 (j),然后用前缀和更新答案。

    至于求出 (j),可以使用桶来处理。


    题外话:至于 (n) 的范围为什么是 37000000,原因如下:

    题目背景:当年陈刀仔,他用 20 块贏到三千七百万,今天我也要从 (n=10) 出到 (n=37000000) !

    Code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mod 1000000007
    #define N 37000005
    using namespace std;
    int T,n,x,y,mex,ans,num,now,fs,a[N],sum[N],near[N];
    bool flag,t[N];
    int getmex()
    {
    	int res=0;
    	while (t[res]) ++res;
    	return res;
    }
    int main()
    {
    	freopen("clods.in","r",stdin);
    	freopen("clods.out","w",stdout);
    	scanf("%d",&T);
    	while (T--)
    	{
    		scanf("%d",&n);
    		mex=0;num=0;
    		if (n!=37000000)
    			for (int i=1;i<=n;++i)
    				scanf("%d",&a[i]);
    		else
    		{
    			scanf("%d%d",&x,&y);
    			for (int i=2;i<=n;++i)
    				a[i]=(a[i-1]*x+y+i)&262143;
    		}
    		for (int i=0;i<=n;++i)
    			t[i]=0;
    		for (int i=1;i<=n;++i)
    			t[a[i]]=1;
    		mex=getmex();
    		if (mex==0)
    		{
    			ans=1;
    			for (int i=1;i<n;++i)
    				ans=ans*2%mod;
    			printf("%d
    ",ans);
    			continue;
    		}
    		for (int i=0;i<mex;++i)
    			t[i]=0,near[i]=-1;
    		for (int i=1;i<=n;++i)
    			if (a[i]<mex)
    			{
    				if (!t[a[i]]) t[a[i]]=1,++num;
    				if (num==mex)
    				{
    					fs=i;
    					break;
    				}
    			}
    		for (int i=0;i<=n;++i)
    			t[i]=0,sum[i]=0;
    		flag=false;now=1;ans=0;
    		for (int i=1;i<=n;++i)
    		{
    			if (a[i]<mex)
    			{
    				if (near[a[i]]!=-1) t[near[a[i]]]=0;
    				near[a[i]]=i;
    				t[i]=1;
    			}
    			if (i==fs) flag=true;
    			while (flag&&!t[now]) ++now;
    			if (flag) ans=sum[now-1]+1;
    			if (ans==mod) ans=0;
    			sum[i]=(sum[i-1]+ans)%mod;
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    GTD180007:【运维】LINUX学习
    GTD180006:【运维】安装调试GDB
    {done}GTD180005:【翻译】LISP prehistory
    ComPiler180001:【学习】编译器学习链接
    AIIE180002:AIIE2015大会主题
    AIIE180001:AIIE2016大会主题
    GTD180004:【开发】python_med
    GTD180003:【开发】python_oeis
    欧亚大帝国及一战、二战
    大洲分界线
  • 原文地址:https://www.cnblogs.com/Livingston/p/15540584.html
Copyright © 2011-2022 走看看