[TIOJ] 1354. 池塘裡的青蛙

題目連結:http://tioj.infor.org/problems/1354
因為不確定題目的範圍所以先開個long long壓壓驚(?,這題也是一樣,矩陣乘法快速冪,因為把圖用鄰接矩陣存下來後,其的n次方就是任意點走到任一點走n步的可能方法總數。只是因為也不確定題目給的n是否遞增,所以就把他排個序,然後再做矩陣快速冪,理論上應該會比較快(?,複雜度$O(t log_2 t + log_2 n)$
#include <stdio.h>
#include <algorithm>
typedef long long lld;

inline void gn(lld &_){_=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')_=_*10+c-'0',c=getchar();}

using std::sort;
using std::swap;

struct inp{
	lld i, val;
};

inline void matrixTimes(lld [][4],lld [][4],lld [][4]);

bool cmp(inp a,inp b){
	return a.val<b.val;
}

int main(){
	lld n;
	gn(n);
	auto v = new inp[n];
	auto ans = new lld[n];
	for(lld i=0;i<n;i++){
		v[i].i=i;
		gn(v[i].val);
	}
	sort(v,v+n,cmp);
	inp k = v[0];
	auto matrix = new lld[4][4];
	auto ta = new lld[4][4];
	auto tb = new lld[4][4];
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			matrix[i][j]=(i==j);
			ta[i][j]=!(i==j);
		}
	}
	while(k.val){
		if(k.val&1){
			auto oao = new lld [4][4];
			matrixTimes(ta,matrix,oao);
			swap(oao,matrix);
		}
		k.val>>=1;
		matrixTimes(ta,ta,tb);
		swap(tb,ta);
	}
	ans[v[0].i] = matrix[0][0];
	for(int i=1;i<n;i++){
		lld k = v[i].val-v[i-1].val;
		auto a = new lld[4][4];
		auto b = new lld[4][4];
		for(int i=0;i<4;i++)
			for(int j=0;j<4;j++)
				a[i][j]=!(i==j);
		while(k){
			if(k&1){
				auto oao = new lld [4][4];
				matrixTimes(a,matrix,oao);
				swap(oao,matrix);
			}
			k>>=1;
			matrixTimes(a,a,b);
			swap(b,a);
		}
		ans[v[i].i] = matrix[0][0];
	}
	for(lld i=0;i<n;i++){
		printf("%lld\n",ans[i]);
	}
	return 0;
}

inline void matrixTimes(lld a[][4],lld b[][4],lld c[][4]){
	for(int i=0;i<4;i++){
		for(int j=0;j<4;j++){
			c[i][j]=0;
			for(int k=0;k<4;k++){
				c[i][j] += a[i][k]*b[k][j];
			}
		}
	}
}

或是這題其實有通式($\frac{3 \times(-1)^n+3^n}{4}$),可以透過把矩陣乘法拆開得到,或是解這個遞迴:$a_0=1,a_1=0, a_n=2 \times a_{n-1} + 3 \times a_{n-2}$
#include <stdio.h>
typedef unsigned long long lld;

inline void gn(lld &_){_=0;char c=getchar_unlocked();while(c<'0'||c>'9')c=getchar_unlocked();while(c>='0'&&c<='9')_=_*10+c-'0',c=getchar_unlocked();}

int main() {
    lld t;
    gn(t);
    while(t--){
      lld k;
      gn(k);
      lld n=k;
      lld ans=1;
      lld a=3;
      while(n){
        if(n&1)
          ans*=a;
        a*=a;
        n>>=1;
      }
      printf("%llu\n",(ans+((k&1)?-3:3))/4);
    }
}

留言

這個網誌中的熱門文章

[NPSC] 2009初賽 E. 檸檬汽水傳說

[TIOJ] 1902. 「殿仁.王,不認識,誰啊?」,然後他就死了……

[AtCoder] [經典競程 90 題] 024 - Select +/- One(★2)