【模型轉化】【dp/矩陣乘法】AGC013E - Placing Squares

題意:

你有一個長度爲n的紙條,上面有m個標記,左端點與第i個標記的距離爲x[i]。現在,你要在紙條上擺放一些(可以爲一個)正方形,滿足以下要求:
1:正方形邊長爲整數。
2:正方形的一條邊必須接觸紙條並且完全處於紙條上(不能超出紙條)。
3:紙條的任易位置都必須接觸正方形(紙條被正方形完全覆蓋)。
4:兩個正方形的邊界線不能直接位於標記之上。
定義一種擺放方式的beauty值爲正方形面積的乘積,你需要求出所有合法的擺放方式的beauty 值之和。

思路:

這道題要用到一個非常巧妙 (根本就想不到) 的模型轉換:
假設我們共放置了 k 個正方形,從左到右邊長依次爲 a1,a2,...,aka_1, a_2, . . . , a_k,依次枚舉aia_i再把它們乘起來顯然是不行的。所以,不妨將這種枚舉邊長類問題轉化爲計數類問題,也就是說,對於邊長爲a1,a2,...,aka_1, a_2, . . . , a_k的一種擺放方式,在等價的計數問題中,我們想要恰好將其統計 a12a22...ak2a_1^2a_2^2 . . . a_k^2次。
所以,我們可以將題目轉化爲如下等價的另一道題:
你有n個空格(從1~ n編號),空格邊緣有n+1個擺放隔板的位置(從0~n編號),其中0號位置和n號位置必須放隔板,而有標記的位置不能放隔板,在每兩個隔板之間的空格上放置一紅一藍兩個小球,同一個格子上可以放兩個小球。求放置隔板和放置小球的方案數。
容易發現,這樣的轉換是等價的,原因如下:
假設擺放隔板的方案已經確定,某兩個隔板之間空格個數爲aia_i,那麼紅色和藍色小球都分別有aia_i种放置的方式,合起來看就有ai2a_i^2种放置的方式,所以當一種擺放隔板的方式已經確定的情況下,擺放小球的方案恰有 a12a22...ak2a_1^2a_2^2 . . . a_k^2種,也就是說,我們將這種方案恰好統計了a12a22...ak2a_1^2a_2^2 . . . a_k^2次。所以,這樣的轉換是等價的。
好了,接下來考慮這道新誕生的題目該怎麼做。
。。。。
計數類問題,很容易想到的當然是dp啦。
定義dp[i][j=0,1,2]dp[i][ j =0,1,2]表示前i個空格已確定,在上一個隔板和這一個空格間已經放jj個小球的方案數。
轉移:
如果第i1i-1個空格和第ii個空格之間不放隔板。
dp[i][0]=dp[i1][0];dp[i][0]=dp[i-1][0];
dp[i][1]=dp[i1][1](i)+dp[i1][0](i)dp[i][1]=dp[i-1][1](第i個位置不放小球)+dp[i-1][0](第i個位置放一個小球)
dp[i][2]=dp[i1][0](i)+dp[i1][1]2(i)+dp[i1][2](i)dp[i][2]=dp[i-1][0](第i個位置放兩個小球)+dp[i-1][1]*2(第i個位置放一個小球,有紅藍和藍紅兩種方式)+dp[i-1][2](第i個位置不放小球)
如果第i1i-1個空格和第ii個空格之間放隔板。由於兩個隔板間必須有兩個小球,所以只能從dp[i1][2]dp[i-1][2]轉移。
dp[i][0]+=dp[i1][2]dp[i][0]+=dp[i-1][2]
dp[i][1]+=dp[i1][2]dp[i][1]+=dp[i-1][2]
dp[i][2]+=dp[i1][2]dp[i][2]+=dp[i-1][2]
於是數據範圍在10510^5內的就解決了,完整代碼如下:

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]);
}

由於n109n≤10^9,所以用矩陣乘法優化。可以放隔板的位置,也就是兩個標記之間的轉移都是一樣的,唯一不同的只是有標記的位置。
構造初始矩陣A,表示如果不能放隔板的轉移:
A=121011001 A= \begin{matrix} 1 &amp; 2 &amp; 1 \\ 0 &amp; 1 &amp; 1 \\ 0 &amp; 0 &amp; 1 \end{matrix}
代表含義:
dp[i][2]dp[i][1]dp[i][0]=Adp[i1][2]dp[i1][1]dp[i1][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}
構造初始矩陣C,表示如果放了隔板的轉移:
C=221111101 C= \begin{matrix} 2 &amp; 2 &amp; 1 \\ 1 &amp; 1 &amp; 1 \\ 1 &amp; 0 &amp; 1 \end{matrix}
代表含義:
dp[i][2]dp[i][1]dp[i][0]=Cdp[i1][2]dp[i1][1]dp[i1][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}
由於兩個標記之間的轉移都屬於放隔板的轉移,所以此處用矩陣快速冪優化。
時間複雜度: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]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章