題目鏈接: 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;
}