题意:
你有一个长度为n的纸条,上面有m个标记,左端点与第i个标记的距离为x[i]。现在,你要在纸条上摆放一些(可以为一个)正方形,满足以下要求:
1:正方形边长为整数。
2:正方形的一条边必须接触纸条并且完全处于纸条上(不能超出纸条)。
3:纸条的任易位置都必须接触正方形(纸条被正方形完全覆盖)。
4:两个正方形的边界线不能直接位于标记之上。
定义一种摆放方式的beauty值为正方形面积的乘积,你需要求出所有合法的摆放方式的beauty 值之和。
思路:
这道题要用到一个非常巧妙 (根本就想不到) 的模型转换:
假设我们共放置了 k 个正方形,从左到右边长依次为
a
1
,
a
2
,
.
.
.
,
a
k
a_1, a_2, . . . , a_k
a1,a2,...,ak,依次枚举
a
i
a_i
ai再把它们乘起来显然是不行的。所以,不妨将这种枚举边长类问题转化为计数类问题,也就是说,对于边长为
a
1
,
a
2
,
.
.
.
,
a
k
a_1, a_2, . . . , a_k
a1,a2,...,ak的一种摆放方式,在等价的计数问题中,我们想要恰好将其统计
a
1
2
a
2
2
.
.
.
a
k
2
a_1^2a_2^2 . . . a_k^2
a12a22...ak2次。
所以,我们可以将题目转化为如下等价的另一道题:
你有n个空格(从1~ n编号),空格边缘有n+1个摆放隔板的位置(从0~n编号),其中0号位置和n号位置必须放隔板,而有标记的位置不能放隔板,在每两个隔板之间的空格上放置一红一蓝两个小球,同一个格子上可以放两个小球。求放置隔板和放置小球的方案数。
容易发现,这样的转换是等价的,原因如下:
假设摆放隔板的方案已经确定,某两个隔板之间空格个数为
a
i
a_i
ai,那么红色和蓝色小球都分别有
a
i
a_i
ai种放置的方式,合起来看就有
a
i
2
a_i^2
ai2种放置的方式,所以当一种摆放隔板的方式已经确定的情况下,摆放小球的方案恰有
a
1
2
a
2
2
.
.
.
a
k
2
a_1^2a_2^2 . . . a_k^2
a12a22...ak2种,也就是说,我们将这种方案恰好统计了
a
1
2
a
2
2
.
.
.
a
k
2
a_1^2a_2^2 . . . a_k^2
a12a22...ak2次。所以,这样的转换是等价的。
好了,接下来考虑这道新诞生的题目该怎么做。
。。。。
计数类问题,很容易想到的当然是dp啦。
定义
d
p
[
i
]
[
j
=
0
,
1
,
2
]
dp[i][ j =0,1,2]
dp[i][j=0,1,2]表示前i个空格已确定,在上一个隔板和这一个空格间已经放
j
j
j个小球的方案数。
转移:
如果第
i
−
1
i-1
i−1个空格和第
i
i
i个空格之间不放隔板。
d
p
[
i
]
[
0
]
=
d
p
[
i
−
1
]
[
0
]
;
dp[i][0]=dp[i-1][0];
dp[i][0]=dp[i−1][0];
d
p
[
i
]
[
1
]
=
d
p
[
i
−
1
]
[
1
]
(
第
i
个
位
置
不
放
小
球
)
+
d
p
[
i
−
1
]
[
0
]
(
第
i
个
位
置
放
一
个
小
球
)
dp[i][1]=dp[i-1][1](第i个位置不放小球)+dp[i-1][0](第i个位置放一个小球)
dp[i][1]=dp[i−1][1](第i个位置不放小球)+dp[i−1][0](第i个位置放一个小球)
d
p
[
i
]
[
2
]
=
d
p
[
i
−
1
]
[
0
]
(
第
i
个
位
置
放
两
个
小
球
)
+
d
p
[
i
−
1
]
[
1
]
∗
2
(
第
i
个
位
置
放
一
个
小
球
,
有
红
蓝
和
蓝
红
两
种
方
式
)
+
d
p
[
i
−
1
]
[
2
]
(
第
i
个
位
置
不
放
小
球
)
dp[i][2]=dp[i-1][0](第i个位置放两个小球)+dp[i-1][1]*2(第i个位置放一个小球,有红蓝和蓝红两种方式)+dp[i-1][2](第i个位置不放小球)
dp[i][2]=dp[i−1][0](第i个位置放两个小球)+dp[i−1][1]∗2(第i个位置放一个小球,有红蓝和蓝红两种方式)+dp[i−1][2](第i个位置不放小球)
如果第
i
−
1
i-1
i−1个空格和第
i
i
i个空格之间放隔板。由于两个隔板间必须有两个小球,所以只能从
d
p
[
i
−
1
]
[
2
]
dp[i-1][2]
dp[i−1][2]转移。
d
p
[
i
]
[
0
]
+
=
d
p
[
i
−
1
]
[
2
]
dp[i][0]+=dp[i-1][2]
dp[i][0]+=dp[i−1][2]
d
p
[
i
]
[
1
]
+
=
d
p
[
i
−
1
]
[
2
]
dp[i][1]+=dp[i-1][2]
dp[i][1]+=dp[i−1][2]
d
p
[
i
]
[
2
]
+
=
d
p
[
i
−
1
]
[
2
]
dp[i][2]+=dp[i-1][2]
dp[i][2]+=dp[i−1][2]
于是数据范围在
1
0
5
10^5
105内的就解决了,完整代码如下:
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]);
}
由于
n
≤
1
0
9
n≤10^9
n≤109,所以用矩阵乘法优化。可以放隔板的位置,也就是两个标记之间的转移都是一样的,唯一不同的只是有标记的位置。
构造初始矩阵A,表示如果不能放隔板的转移:
A
=
1
2
1
0
1
1
0
0
1
A= \begin{matrix} 1 & 2 & 1 \\ 0 & 1 & 1 \\ 0 & 0 & 1 \end{matrix}
A=100210111
代表含义:
d
p
[
i
]
[
2
]
d
p
[
i
]
[
1
]
d
p
[
i
]
[
0
]
=
A
∗
d
p
[
i
−
1
]
[
2
]
d
p
[
i
−
1
]
[
1
]
d
p
[
i
−
1
]
[
0
]
\begin{matrix} dp[i][2] \\ dp[i][1] \\ dp[i][0] \end{matrix}=A*\begin{matrix} dp[i-1][2] \\ dp[i-1][1] \\ dp[i-1][0] \end{matrix}
dp[i][2]dp[i][1]dp[i][0]=A∗dp[i−1][2]dp[i−1][1]dp[i−1][0]
构造初始矩阵C,表示如果放了隔板的转移:
C
=
2
2
1
1
1
1
1
0
1
C= \begin{matrix} 2 & 2 & 1 \\ 1 & 1 & 1 \\ 1 & 0 & 1 \end{matrix}
C=211210111
代表含义:
d
p
[
i
]
[
2
]
d
p
[
i
]
[
1
]
d
p
[
i
]
[
0
]
=
C
∗
d
p
[
i
−
1
]
[
2
]
d
p
[
i
−
1
]
[
1
]
d
p
[
i
−
1
]
[
0
]
\begin{matrix} dp[i][2] \\ dp[i][1] \\ dp[i][0] \end{matrix}=C*\begin{matrix} dp[i-1][2] \\ dp[i-1][1] \\ dp[i-1][0] \end{matrix}
dp[i][2]dp[i][1]dp[i][0]=C∗dp[i−1][2]dp[i−1][1]dp[i−1][0]
由于两个标记之间的转移都属于放隔板的转移,所以此处用矩阵快速幂优化。
时间复杂度:
O
(
m
l
o
g
n
)
O(mlogn)
O(mlogn)
#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]);
}