bzoj4490 随机数生成器Ⅱ加强版

题目链接

题意

给出参数(C_1,C_2,P)按如下方式生成一个长度为(n imes m)的序列(x):

(x_0 = C_1,x_1=C2)

(x_i=(x_{i-1}+x_{i-1}) \% P ; (i > 1))

然后按如下方式生成一个长度为(n imes m)的序列(a)

[a_i=sumlimits_{j=0}^ix_j^2\%P ]

然后现在进行(Q)次操作,每次操作给出两个参数(k1,k2)。表示交换(k1,k2)的值。

然后把序列(a)按顺序放到一个(n imes m)的网格中。

要求出一种方案,使得从((1,1))走到((n,m)),所经过的数字排列起来字典序最小。

思路

容易发现,其实就是快速求出(a_i)

然后就推一下式子。

[x_i^2=x_{i-1}^2+2x_{i-1}x_{i-2}+x_{i-2}^2 ]

[x_{i-1}^2=x_i^2-x_{i-2}^2-2x_{i-1}x_{i-2} ]

[x_{i-1}^2=(x_i+x_{i-2}) imes(x_i-x_{i-2}) - 2x_{i-1}x_{i-2} ]

[x_{i-1}^2=x_{i-1}(x_i+x_{i-2}) - 2x_{i-1}x_{i-2} ]

[x_{i-1}^2=x_ix_{i-1}+x_{i-1}x_{i-2}-2x_{i-1}x_{i-2} ]

[x_{i-1}^2=x_ix_{i-1}-x_{i-1}x_{i-2} ]

也就是说

[x_i^2=x_{i+1}x_i-x_ix_{i-1} ]

这样也就是容易得到

[a_i=x_{i+1}x_i-C_1(C_2-C_1) ]

所以只要可以快速的求出斐波那契数列第(i)项就可以了。

如果直接每次矩阵快速幂会(TLE)

所以先预处理出(fbi_1,fbi_2,fbi_3...fbi_m)(fbi_m,fbi_{2m},fbi_{3m}...fbi{nm})的转移矩阵。

对于第(i)行第(j)列的数,直接(O(2^3))求出

代码

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<cstring>
#include<bitset>
#include<map>
using namespace std;
typedef long long ll;
const int N = 500000 + 100,INF = 1e9 + 7;
map<ll,ll>ma;
ll read() {
	ll x=0,f=1;char c=getchar();
	while(c<'0'||c>'9') {
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9') {
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}
int C1,C2,mod,n,m,Q;
namespace FBI {
	struct node {
		int a[3][3],n,m;
		node() {
			memset(a,0,sizeof(a));
		}
		node(int x) {
			n = m = x;
			memset(a,0,sizeof(a));
			for(int i = 1;i <= x;++i) a[i][i] = 1;
		}
		node(int x,int y) {
			n = x,m = y;
			memset(a,0,sizeof(a));
		}
	};
	node operator * (const node &A,const node &B) {
		int n = A.n,m = B.m,K = A.m;
		node ret(n,m);
		for(int k = 1;k <= K;++k) {
			for(int i = 1;i <= n;++i) {
				for(int j = 1;j <= m;++j) {
					ret.a[i][j] += 1ll * A.a[i][k] * B.a[k][j] % mod;
					ret.a[i][j] %= mod;
				}
			}
		}
		return ret;
	}
	node fbi(1,2),tmp(2,2),lin[N],row[N];
	void pre() {
		fbi.a[1][1] = C2,fbi.a[1][2] = C1;
		tmp.a[1][1] = tmp.a[1][2] = tmp.a[2][1] = 1;
		
		row[0].n = row[0].m = 2;row[0].a[1][1] = row[0].a[2][2] = 1;
		for(int i = 1;i <= m;++i) row[i] = row[i - 1] * tmp;


		lin[0].n = lin[0].m = 2;
		lin[0].a[1][1] = lin[0].a[2][2] = 1;
		for(int i = 1;i <= n;++i) lin[i] = lin[i - 1] * row[m];
	}
	int solve(ll x) {
		if(x == -1) return (C2 - C1 + mod) % mod;
		if(x == 0) return C1;
		if(x == 1) return C2;
		--x;
		int y = x % m;
		x /= m;
		return (fbi * lin[x] * row[y]).a[1][1];
	}
	int main(ll x) {
		return (1ll * solve(x + 1) * solve(x) % mod - 1ll * C1 * (C2 - C1) % mod + mod) % mod;
	}
}
ll p(ll x,ll y) {
	ll z = (x - 1) * m + y;
	if(ma[z]) return ma[z];
	return z;
}
ll cnt = 1,ans;
void solve(int x,int y) {
	++cnt;
	if(x == n && y == m) return;
	int down = INF,right = INF;
	if(x != n) down = FBI::main(p(x + 1,y));
	if(y != m) right = FBI::main(p(x,y + 1));
	if(down <= right) {
		ans += cnt * down % mod;
		ans %= mod;
		solve(x + 1,y);
	}
	else {
		ans += cnt * right % mod;
		ans %= mod;
		solve(x,y + 1);
	}
	return;
}
int main() {
	n = read(),m = read(),Q = read(),mod = read(),C1 = read(),C2 = read();
	for(int i = 1;i <= Q;++i) {
		ll x = read(),y = read(),tx = x,ty = y;
		if(ma[x]) tx = ma[x];
		if(ma[y]) ty = ma[y];
		ma[x] = ty;ma[y] = tx;
	}
	FBI::pre();
	if(ma[1]) ans += FBI::main(ma[1]),ans %= mod;
	else ans += FBI::main(1),ans %= mod;
	solve(1,1);
	cout<<ans;
	return 0;
}
原文地址:https://www.cnblogs.com/wxyww/p/bzoj4490.html