題意:
你有一個長度爲n的紙條,上面有m個標記,左端點與第i個標記的距離爲x[i]。現在,你要在紙條上擺放一些(可以爲一個)正方形,滿足以下要求:
1:正方形邊長爲整數。
2:正方形的一條邊必須接觸紙條並且完全處於紙條上(不能超出紙條)。
3:紙條的任易位置都必須接觸正方形(紙條被正方形完全覆蓋)。
4:兩個正方形的邊界線不能直接位於標記之上。
定義一種擺放方式的beauty值爲正方形面積的乘積,你需要求出所有合法的擺放方式的beauty 值之和。
思路:
這道題要用到一個非常巧妙 (根本就想不到) 的模型轉換:
假設我們共放置了 k 個正方形,從左到右邊長依次爲 ,依次枚舉再把它們乘起來顯然是不行的。所以,不妨將這種枚舉邊長類問題轉化爲計數類問題,也就是說,對於邊長爲的一種擺放方式,在等價的計數問題中,我們想要恰好將其統計 次。
所以,我們可以將題目轉化爲如下等價的另一道題:
你有n個空格(從1~ n編號),空格邊緣有n+1個擺放隔板的位置(從0~n編號),其中0號位置和n號位置必須放隔板,而有標記的位置不能放隔板,在每兩個隔板之間的空格上放置一紅一藍兩個小球,同一個格子上可以放兩個小球。求放置隔板和放置小球的方案數。
容易發現,這樣的轉換是等價的,原因如下:
假設擺放隔板的方案已經確定,某兩個隔板之間空格個數爲,那麼紅色和藍色小球都分別有种放置的方式,合起來看就有种放置的方式,所以當一種擺放隔板的方式已經確定的情況下,擺放小球的方案恰有 種,也就是說,我們將這種方案恰好統計了次。所以,這樣的轉換是等價的。
好了,接下來考慮這道新誕生的題目該怎麼做。
。。。。
計數類問題,很容易想到的當然是dp啦。
定義表示前i個空格已確定,在上一個隔板和這一個空格間已經放個小球的方案數。
轉移:
如果第個空格和第個空格之間不放隔板。
如果第個空格和第個空格之間放隔板。由於兩個隔板間必須有兩個小球,所以只能從轉移。
於是數據範圍在內的就解決了,完整代碼如下:
void task1()
{
static int dp[100010][3];
dp[0][0]=1;
for(int i=1;i<=m;i++)
vis[x[i]]=1;
for(int i=1;i<=n;i++)
{
dp[i][0]=dp[i-1][0];
dp[i][1]=(dp[i-1][0]+dp[i-1][1])%MO;
dp[i][2]=((dp[i-1][0]+dp[i-1][1])%MO+(dp[i-1][1]+dp[i-1][2])%MO)%MO;
if(!vis[i-1])
{
dp[i][0]=(dp[i][0]+dp[i-1][2])%MO;
dp[i][1]=(dp[i][1]+dp[i-1][2])%MO;
dp[i][2]=(dp[i][2]+dp[i-1][2])%MO;
}
}
printf("%d\n",dp[n][2]);
}
由於,所以用矩陣乘法優化。可以放隔板的位置,也就是兩個標記之間的轉移都是一樣的,唯一不同的只是有標記的位置。
構造初始矩陣A,表示如果不能放隔板的轉移:
代表含義:
構造初始矩陣C,表示如果放了隔板的轉移:
代表含義:
由於兩個標記之間的轉移都屬於放隔板的轉移,所以此處用矩陣快速冪優化。
時間複雜度:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define MAXN 100010
#define MO 1000000007
#define LL long long
int x[MAXN],n,m;
bool vis[MAXN];
void task1()
{
static int dp[100010][3];
dp[0][0]=1;
for(int i=1;i<=m;i++)
vis[x[i]]=1;
for(int i=1;i<=n;i++)
{
dp[i][0]=dp[i-1][0];
dp[i][1]=(dp[i-1][0]+dp[i-1][1])%MO;
dp[i][2]=((dp[i-1][0]+dp[i-1][1])%MO+(dp[i-1][1]+dp[i-1][2])%MO)%MO;
if(!vis[i-1])
{
dp[i][0]=(dp[i][0]+dp[i-1][2])%MO;
dp[i][1]=(dp[i][1]+dp[i-1][2])%MO;
dp[i][2]=(dp[i][2]+dp[i-1][2])%MO;
}
}
printf("%d\n",dp[n][2]);
}
struct node
{
LL a[3][3];
}A,C,a,b,c,tmp;
void Mul(node &ret,node X,node Y)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
tmp.a[i][j]=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
for(int k=0;k<3;k++)
tmp.a[i][j]=(tmp.a[i][j]+X.a[i][k]*Y.a[k][j]%MO)%MO;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
ret.a[i][j]=tmp.a[i][j];
}
void Pow(int X)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
c.a[i][j]=C.a[i][j];
while(X)
{
if(X&1) Mul(a,a,c);
Mul(c,c,c);
X>>=1;
}
}
int f[3],g[3];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d",&x[i]);
memset(A.a,0,sizeof A.a);
memset(C.a,0,sizeof C.a);
A.a[0][0]=A.a[1][1]=A.a[2][2]=A.a[1][2]=A.a[0][2]=1;
A.a[0][1]=2;
C.a[0][2]=C.a[1][0]=C.a[1][1]=C.a[1][2]=C.a[2][0]=C.a[2][2]=1;
C.a[0][0]=C.a[0][1]=2;
f[0]=1;
x[++m]=n;
for(int l=1,pos=0;l<=m;l++)
{
int k=x[l]-pos-1;
memset(b.a,0,sizeof b.a);
memset(a.a,0,sizeof a.a);
for(int i=0;i<3;i++)
a.a[i][i]=1;
if(k) Pow(k);
Mul(b,a,A);
for(int i=0;i<3;i++)
{
g[i]=0;
for(int j=0;j<3;j++)
g[i]=(g[i]+f[j]*b.a[j][i]%MO)%MO;
}
for(int i=0;i<3;i++)
f[i]=g[i];
pos=x[l];
}
printf("%d\n",f[2]);
}