1. 程式人生 > >CF917C Pollywog —— 狀壓DP + 矩乘優化

CF917C Pollywog —— 狀壓DP + 矩乘優化

答案 得到 clas ons 就是 時間 .com sum struct

C. Pollywog

題目描述

  原題題目鏈接。題目大意為:有$x$只蝌蚪,在$n$個石頭中的最左端的$x$個石頭上,這$n$個石頭是在同一直線上的。每一次只能最左邊的一個蝌蚪進行跳躍,並且只能跳$1$至$k$步,跳$i$步要花費$c_i$的體力。在蝌蚪跳躍時,是不能跳到已有蝌蚪的石頭上。在這$n$個石頭裏面有$q$個石頭是特殊的石頭,跳上這$q$個石頭中的第$p$個要額外花費$w_p$的體力值。問當所有蝌蚪跳到最右邊的$x$個石頭時的最小體力值。

思路

  首先,根據題意我們能知道所有的蝌蚪都一定在連續的$k$塊石頭上。現在進行一個小小的證明:假設這些蝌蚪不在連續的$k$塊石頭上,則最左邊的蝌蚪在跳躍時一定會跳在最右邊的蝌蚪的左邊,不會使最右端點向右移動,但是因為最左邊的蝌蚪跳動了,所以最左端點會向右移動,這樣的話,區間就會縮短。知道當最左邊的蝌蚪和最右邊的蝌蚪的距離小於$k$時,才能在向右跳躍時將最右端點向右移動,又因為開始的時候所有的蝌蚪都在最左邊的$x$個石頭上,所以不會使區間大於$k$。

  因此我們就可以處理出來長度為$k$的所有的情況之間的轉移。我們定義一個單位元矩陣,$num[i][j]$表示狀態為$i$的石頭轉移成為狀態為$j$的石頭的花費。什麽是狀態為$i$的石頭呢???我們將$i$轉成二進制,這樣我們就得到了一個$01$串,在這個$01$串中,$0$表示這個石頭上沒有蝌蚪,反之$1$表示有蝌蚪。因為我們一共就只需要枚舉$k$塊石頭,並且只有$x$只青蛙,且$k,x \le 8$,所以最多就只有$C_8^4$種情況,將二進制串離散一下就好了。這就是預處理。

  我們得到了一個轉移的鄰接矩陣,我們就可以用這個矩陣來進行矩乘,先不考慮特殊的石頭,所以就計算這個矩陣的$n-x$次冪就可以,$n-x$次冪的意思是,每一次都將當前$k$塊石頭向右進行移動一塊石頭,這樣移動$n-x$次就是答案,但是特殊的石頭要更改答案,所以我們到達一塊特殊的石頭,就停下來暴力就可以了。我們知道一塊特殊的石頭只能對$k$個轉移帶來影響。所以我們暴力停下來轉移是可以的,時間復雜的是$O(k^2 \times q \times C_k^x)$。

代碼

#include <cstdio>
#include <algorithm>
using namespace std;
#define inf 1e18
#define N 10
int x,k,n,q,cnt;long long num[N];int bel[1<<10];
struct Square
{
	long long num[71][71];
	Square()
	{for(int i=1;i<=70;i++) {for(int j=1;j<=70;j++) num[i][j]=inf;num[i][i]=0;}}
	Square operator * (const Square &a) const
	{
		Square tmp;
		for(int i=1;i<=cnt;i++)
			tmp.num[i][i]=inf;
		for(int i=1;i<=cnt;i++)
			for(int j=1;j<=cnt;j++)
				for(int k=1;k<=cnt;k++)
					tmp.num[i][j]=min(tmp.num[i][j],num[i][k]+a.num[k][j]);
		return tmp;
	}
	Square operator ^ (const int &x) 
	{
		if(!x) return Square();
		Square tmp,tmp1;int times=x;
		for(int i=1;i<=cnt;i++)
			for(int j=1;j<=cnt;j++) tmp1.num[i][j]=num[i][j];
		while(times) {if(times&1) tmp=tmp*tmp1;times>>=1;tmp1=tmp1*tmp1;}
		return tmp;
	}
}one;
struct Stone
{int place;long long val;}stone[30];
bool calc(int tmp)
{
	int many=0;
	for(int i=1;i<=8;i++) if(tmp&(1<<(i-1))) many++;
	return many==x;
}
bool cmp(const Stone &a,const Stone &b)
{return a.place<b.place;}
int main()
{
	scanf("%d%d%d%d",&x,&k,&n,&q);
	for(int i=0;i<=(1<<k)-1;i++) if(calc(i)) bel[i]=++cnt;
	for(int i=1;i<=k;i++) scanf("%lld",&num[i]);
	for(int i=1;i<=q;i++) scanf("%d%lld",&stone[i].place,&stone[i].val);
	sort(stone+1,stone+q+1,cmp);
	for(int i=1;i<=cnt;i++) one.num[i][i]=inf;
	for(int i=1;i<=(1<<k)-1;i++)
	{
		if(!bel[i]) continue;
		if(i&1)
		{for(int j=1;j<=k;j++)
			{if(!((1<<j)&i)) one.num[bel[i]][bel[((1<<j)|i)>>1]]=num[j];}}
		else one.num[bel[i]][bel[i>>1]]=0;
	}
	int now=1;long long sum=0;Square ans;
	for(int i=1;i<=q;i++)
	{
		if(stone[i].place>n-x) {sum+=stone[i].val;continue;}
		ans=ans*(one^(stone[i].place-now)),now=stone[i].place;
		for(int j=1;j<=(1<<k)-1;j+=2)
			if(bel[j])
				for(int k=1;k<=cnt;k++)
					ans.num[k][bel[j]]+=stone[i].val;
	}
	ans=ans*(one^(n-x+1-now));
	printf("%lld",ans.num[1][1]+sum);
}

  

CF917C Pollywog —— 狀壓DP + 矩乘優化