递推集合2

T1:划分数列

设 fi表示从一到 i 最少可以划分的段数,fi =min ( fbi1 , fci1 ) +1

bi​ 表示以 i 结尾单调递增的一段的起点,ci 表示以 i 结尾单调递减的一段的起点

查看代码
int n,last,a,b[100005],c[100005],f[100005];
int main()
{
	scanf("%d%d",&n,&last);
	b[1]=1;
	c[1]=1;
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&a);
		if(a>=last) b[i]=b[i-1];
		else b[i]=i;
		if(a<=last) c[i]=c[i-1];
		else c[i]=i;
		last=a;
	}
	memset(f,1e7,sizeof(f));
	f[0]=0;
	f[1]=0;
	for(int i=1;i<=n;i++) f[i]=min(f[b[i]-1]+1,f[c[i]-1]+1); 
	printf("%d",f[n]);
	return 0;
}

T2:求 f 函数

真水

查看代码
int main()
{
	int n;
	while(1)
	{
		scanf("%d",&n);
		if(n==0)break;
		if(n<=101)printf("91\n");
		else printf("%d\n",n-10);
	}
	return 0;
}

T3:无限序列

每第(i)次变化得来的数列为第(i-1)次+第(i-2)次
所以,相同地,第(i)个数列中1的数量为第(i-1)次+第(i-2)次。

由于a ~ b可以看为:(1~b) - (1 ~ a-1),前缀和,
1~i的数列中1的个数,可以看成是经过某两次变化得来的1的个数的和(a+b),其中这分别两次变化得来的数列的长度的和必须为i。

查看代码

int Q;
long long a,b,p[105];
long long f(long long x)
{
    long long ans=0,j;    
    while(x)
    {
        j=1;
        while(p[j+2]<=x) j++; 
        ans+=p[j];
        x-=p[j+1];
    }
    return ans;
}
int main()
{
    p[1]=1;p[2]=1;
    for(int i=3;i<=100;i++) p[i]=p[i-1]+p[i-2];
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
        scanf("%lld%lld",&a,&b);
        printf("%lld\n",f(b)-f(a-1));
    }
    return 0;
}

T4:序列个数

以序列 2 3 4 1 5 为例,横轴为数字,纵轴为位置,辣么 ai 可视为以(1,1)为左上角,(i,i)为右下角的矩阵中 1 的个数

 ai - ai-1 表示一个 L 字形区域内 1 的个数。考虑从内到外一次填入 1,分为三种情况

y \ x    1       2    3     4    5
   1    0    1    0    0    0
   2    0    0    1    0    0
   3    0    0    0    1    0
   4    1    0    0    0    0
   5    0    0    0    0    0

 

查看代码
ll n, a[101000], mod=340610;
int main()
{
	scanf("%lld", &n);
	ll ans=1;
	for(ll i=1; i<=n; i++)
	{
		scanf("%lld", &a[i]);
		if(a[i]-a[i-1]==1) ans=(ans*(i*2-1-2*a[i-1]))%mod;         // 共2*I-1个位置,内层已经放a[i-1]个互不冲突的1
		if(a[i]-a[i-1]==2) ans=(ans*(i-a[i-1]-1)*(i-a[i-1]-1))%mod;// 两条臂上各放一个 1,内层已经放a[i-1]个互不冲突的1
		if(a[i]-a[i-1]>2) ans=0;
	}
	printf("%lld",ans);
	return 0;
}

T5:平铺方案

第i列上竖着放一个2 * 1的,方案数为 f(i-1)
第i列与第i-1列放一个2 * 2的,方案数为 f(i-2)
第i列与第i-1列横着放一个1 * 2的,方案数为 f(i-2)

至于那个漂亮的高精小板子,来自之前积累的当我想敲高精加

查看代码

#include<bits/stdc++.h>
using namespace std;
int f[300][100010],len[10010],t[10010]; 
void add(int k)
{
	int x=0;
	for(int i=0;i<=len[k-2];i++)
	{
		f[k][i]=f[k-2][i]+f[k-2][i]+x;
		x=f[k][i]/10;
		f[k][i]%=10;
	}
	len[k]=len[k-2];
	if(x) f[k][++len[k]]=x;
	x=0;
	len[k]=max(len[k],len[k-1]);
	for(int i=0;i<=len[k];i++)
	{
		f[k][i]=f[k][i]+f[k-1][i]+x;
		x=f[k][i]/10;
		f[k][i]%=10;
	}
	if(x) f[k][++len[k]]=x;
}
int main()
{
	len[0]=len[1]=len[2]=0;
	f[1][0]=1;
	f[2][0]=3;
	int q;
	for(int i=3;i<=251;i++) add(i);
	while(cin>>q)
	{
		for(int i=len[q];i>=0;i--) printf("%d",f[q][i]);
		printf("\n");
	}
	return 0;
}
posted @ 2022-02-17 12:37  fervency  阅读(19)  评论(0编辑  收藏  举报