模拟赛4 数字序列 50分做法 状压DP

题意:求由1到k之间的数字组成的,满足如果ai=aj,则i-j>=ai的序列个数

一脸懵逼在比赛中想不出AC方法,50分做法:考虑状压,因为新的一位能放哪些数字只和最后m-1位有关,又因为k<=7,于是考虑状压.

在50分数据也就是k=5时刚好能跑出来

时间复杂度O(n*k^k),大数据直接爆炸= =毕竟太弱

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<bitset>
#define LL long long
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
inline LL read()
{
	LL d=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
	return d*f;
}
#define N 10005
#define M 8
#define inf 910109
int f[2][10000];
struct S
{
	int x,y;
}s[1000];
int n,m,num=0,ma=0;
int po8[10];
int c[9];

void dfs(int k)
{
	if(k>m)
	{
		int x=0,y=0;
		fo(i,1,m)
		{
			if(i!=m)x+=c[i]*po8[i-1];
			if(i!=1)y+=c[i]*po8[i-2];
		}
//		cout<<num+1<<endl;
//		fo(i,1,m)cout<<c[i]<<' ';cout<<endl;
		s[++num].x=x;s[num].y=y;
//		f[1-m%2][x]++;
		ma=max(ma,max(x,y));
		return;
	}
	bitset<10>a;a.reset();c[k]=1;dfs(k+1);c[k]=0;
	for(int i=k-1,j=2;j<=m;i--,j++)
	{
		if(i>0)a[c[i]]=1;
		if(a[j]==0)
		{
			c[k]=j;
			dfs(k+1);
			c[k]=0;
		}
	}
}
void prework()
{
	po8[0]=1;
	fo(i,1,7)po8[i]=po8[i-1]*8;
	memset(c,0,sizeof(c));
	memset(f,0,sizeof(f));
	dfs(1);
	fo(i,1,num)f[1-m%2][s[i].x]=1;
}

int main()
{
	n=read(),m=read();
	prework();
	fo(i,m,n)
	{
		fo(j,1,num)
		{
			f[i%2][s[j].x]=0;
			f[i%2][s[j].y]=0;
		}
		fo(j,1,num)
		{
			f[i%2][s[j].y]+=f[(i+1)%2][s[j].x];
			if(f[i%2][s[j].y]>=inf)f[i%2][s[j].y]%=inf;
		}
	}
	int ans=0;
	fo(i,1,3000)
	{
		ans+=f[n%2][i];
		if(ans>=inf)ans%=inf;
	}
	
	cout<<ans<<endl;
	return 0;
}


你可能感兴趣的:(dp)