[HNOI2002]公交车路线

13 篇文章 1 订阅
4 篇文章 0 订阅

挑战全网最快!!

做法一O(N)

直接递推, d p [ i ] [ j ] dp[i][j] dp[i][j]表示Tiger在换了i次车时位于j的方案数,最后输出 d p [ n ] [ E ] dp[n][E] dp[n][E]

由于这是2002年的题,所以用现在的评测机O(N)也能过

做法二O(8^3logN)

矩阵乘法,转移矩阵如下

01000001
10100000
01010000
00101000
00000000
00001010
00000101
10000010

不说了。

做法三O(5^3logN)

由于这个图左右是对称的,所以B和H,C和G,D和F的方案数都是一样的,所以只需要存5个点就好了。

转移矩阵就不放了,和做法二类似。

做法四O(2^2logN)

由于此题图形都是一样的,所以最终答案是有通项公式的!

然后我们先写出递推式:

  • a ( 0 ) = 1 a(0)=1 a(0)=1
  • a ( n ) = 2 b ( n − 1 ) a(n)=2b(n-1) a(n)=2b(n1)
  • b ( n ) = a ( n − 1 ) + c ( n − 1 ) b(n)=a(n-1)+c(n-1) b(n)=a(n1)+c(n1)
  • c ( n ) = b ( n + 1 ) + d ( n − 1 ) c(n)=b(n+1)+d(n-1) c(n)=b(n+1)+d(n1)
  • d ( n ) = c ( n − 1 ) d(n)=c(n-1) d(n)=c(n1)
  • e ( n ) = 2 d ( n − 1 ) e(n)=2d(n-1) e(n)=2d(n1)

写出生成函数(OGF),和生成函数之间的5个方程

  • A ( z ) = 2 z B ( z ) + 1 A(z)=2zB(z)+1 A(z)=2zB(z)+1
  • B ( z ) = z A ( z ) + z C ( z ) B(z)=zA(z)+zC(z) B(z)=zA(z)+zC(z)
  • C ( z ) = z B ( z ) + z D ( z ) C(z)=zB(z)+zD(z) C(z)=zB(z)+zD(z)
  • D ( z ) = z C ( z ) D(z)=zC(z) D(z)=zC(z)
  • E ( z ) = 2 z D ( z ) E(z)=2zD(z) E(z)=2zD(z)

解关于 A ( z ) , B ( z ) , C ( z ) , D ( z ) , E ( z ) A(z),B(z),C(z),D(z),E(z) A(z),B(z),C(z),D(z),E(z),含参数 z z z的五元一次方程组,得:
E ( z ) = 2 z 4 1 − 4 z 2 + 2 z 4 E(z)=\frac{2z^4}{1-4z^2+2z^4} E(z)=14z2+2z42z4
因式分解:
k 1 = 2 + 2 , k 2 = 2 − 2 k_1=2+\sqrt2,k_2=2-\sqrt2 k1=2+2 ,k2=22
E ( z ) = 2 z 4 ( 1 − k 1 z 2 ) ( 1 − k 2 z 2 ) E(z)=\frac{2z^4}{(1-k_1z^2)(1-k_2z^2)} E(z)=(1k1z2)(1k2z2)2z4
我们来好好理解一下这个式子:

  1. 分子是个最低次为4次的多项式,因为从A到E最少要换乘4次
  2. 分母中分解后的两个多项式中每项的次数皆为偶数因为从A到E换乘的次数必然为偶数,如果是奇数那么肯定到不了E(染色法可证)

所以我们可以直接略过 e ( 2 n + 1 ) e(2n+1) e(2n+1)(这些全是0),只用计算 e ( 2 n ) e(2n) e(2n)即可。

让我们直接写出 e ( 2 n ) e(2n) e(2n)的生成函数(OGF) F ( z ) F(z) F(z)
F ( z ) = 2 z 2 ( 1 − k 1 z ) ( 1 − k 2 z ) F(z)=\frac{2z^2}{(1-k_1z)(1-k_2z)} F(z)=(1k1z)(1k2z)2z2只需要满足 F ( z 2 ) = E ( z ) F(z^2)=E(z) F(z2)=E(z)即可

定义 f ( n ) = e ( 2 n ) f(n)=e(2n) f(n)=e(2n),则 f ( 1 ) = f ( 0 ) = 0 f(1)=f(0)=0 f(1)=f(0)=0(因为只通过2次换乘到不了E)

所以直接把 z 2 z^2 z2从分母中提出来:
F ( z ) = z 2 2 ( 1 − k 1 z ) ( 1 − k 2 z ) F(z)=z^2\frac{2}{(1-k_1z)(1-k_2z)} F(z)=z2(1k1z)(1k2z)2

再根据有理生成函数的一般展开定理:
f ( n + 2 ) = a 1 ∗ k 1 n + a 2 ∗ k 2 n f(n+2)=a_1*{k_1}^n+a_2*{k_2}^n f(n+2)=a1k1n+a2k2n
已知两个等式(一个显而易见,一个是样例给的):
f ( 2 ) = 2 , f ( 3 ) = 8 f(2)=2,f(3)=8 f(2)=2,f(3)=8代入 f ( n + 2 ) = a 1 ∗ k 1 n + a 2 ∗ k 2 n f(n+2)=a_1*{k_1}^n+a_2*{k_2}^n f(n+2)=a1k1n+a2k2n就能求出系数 a 1 , a 2 a_1,a_2 a1,a2了!!胜利在望

解得 a 1 = 1 + 2 , a 2 = 1 − 2 a_1=1+\sqrt2,a_2=1-\sqrt2 a1=1+2 ,a2=12

所以最后的通项公式为:
f ( n + 2 ) = ( 1 + 2 ) ( 2 + 2 ) n + ( 1 − 2 ) ( 2 − 2 ) n f(n+2)=(1+\sqrt2)(2+\sqrt2)^n+(1-\sqrt2)(2-\sqrt2)^n f(n+2)=(1+2 )(2+2 )n+(12 )(22 )n
由于两个可以表示成 a 2 + b a\sqrt2+b a2 +b的数相乘还是可以表示成 a 2 + b a\sqrt2+b a2 +b,所以把原来的乘法改成4次乘法然后快速幂就可以了。

上代码!

#include<bits/stdc++.h>
using namespace std;
namespace fastio{
	template<typename tn> void read(tn &a){
	    tn x=0,f=1;char c=' ';
	    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
	    for(;isdigit(c);c=getchar() ) x=x*10+c-'0';
	    a=x*f;
	}
	template<typename tn> void print(tn a){
	    if(a<0) putchar('-'),a=-a;
	    if(a>9) print(a/10);
	    putchar(a%10+'0');
	}
};
using namespace fastio;
const int mod=1000;
struct num{
	int a,b;
	num operator *(const num x)const{
		num ret;
		ret.b=(2*x.a*a+x.b*b)%mod;
		ret.a=(x.a*b+x.b*a)%mod; 
		return ret;
	}
	num operator +(const num x)const{
		num ret;
		ret.a=(x.a+a)%mod;
		ret.b=(x.b+b)%mod; 
		return ret;
	}
};
int n;
num poww(num a,int b){
	num ans=(num){0,1};
	while(b){
		if(b&1) ans=ans*a;
		a=a*a;
		b>>=1; 
	}
	return ans;
}
int main(){
	read(n);
	if(n&1){
		puts("0");
		return 0;
	}
	n>>=1;
	n-=2;
	if(n<0){
		puts("0");
		return 0;
	}
	num ans=(num){1,1}*poww((num){1,2},n);
	printf("%d\n",ans.b*2%mod);
	return 0;
}

提交!

然而并没有比矩阵乘快到那里去(我杀我自己)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值