题目链接: https://ac.nowcoder.com/acm/contest/1099/D
题意:
你要构造一个长为 的数字串 ,使得其满足 个条件,每个条件为一个区间 要求 ,问你能构造出多少这样的串。
做法:
把题目剖析一下大概就是,在这些区间中的数字必须要至少有一个为 或者分解质因数后有两个 。那么我们就可以把数字 当做数字 ,数字 当做数字 。即要求每个区间内的和要至少为 。
队友用的是记忆化搜索,大概意思 是到达第 个数字时,数字和为 的第一个区间为 ,和为 的第一个区间为 。听起来能懂的样子然而…我是不会敲,膜一下膜一下…
后来看了人家的代码学到了另一种稍微简单一点的做法。
我们先维护好每一个右端点最大限制的左界在哪里(即对于位置 ,要求的最近的左区间位置在哪里),同样以上面的理论作为基础 代表的是,到第 个格子的时候,之前的第一个 出现的位置为 ,第二个 出现的位置为 的方案数。
当然,我们需要一些前置条件限制,如果我们想要从 转移来,那么这个 必要满足 时候的情况。 如果已经满足,那假设我们这一位放的是 , 那么最近的就可以变成 即最近的有位置 一份,假设我们这一位放的是 , 那么最近的就可以变成 ,即两个最近的 都已经是我当前枚举的位置了。然后就转移就可以了。
代码
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
const int N=55;
const ll mod=1e9+7;
ll dp[55][55][55],ans;
int n,m,L[55];
void add(ll &a,ll b){
a=(a+b)%mod;
}
void init(){
ans=0ll;
memset(L,0,sizeof(L));
memset(dp,0,sizeof(dp));
dp[1][1][1]=2ll;dp[1][1][0]=2ll;
dp[1][0][0]=6ll;
}
int main(){
while(~scanf("%d%d",&n,&m)){
init();
rep(i,1,m){
int x,y; scanf("%d%d",&x,&y);
L[y]=max(L[y],x);
}
rep(i,2,n){
rep(j,0,i){
rep(k,0,j){
if(k<L[i-1]) continue;
add(dp[i][j][k],6ll*dp[i-1][j][k]);
add(dp[i][i][j],2ll*dp[i-1][j][k]);
add(dp[i][i][i],2ll*dp[i-1][j][k]);
}
}
}
rep(j,0,n){
rep(k,0,j){
if(k<L[n]) continue;
add(ans,dp[n][j][k]);
}
}
printf("%lld\n",ans);
}
return 0;
}