【矩阵快速幂】P8944 The Da Vinci Code|普及+

本文涉及知识点

【矩阵快速幂】封装类及测试用例及样例

P8944 The Da Vinci Code

题目背景

圣杯在罗斯琳教堂下静待。
大师杰作掩映中相拥入眠。
剑刃圣杯守护着她的门宅。
星空下她可安息无碍。

好的题目不需要花里胡哨的背景。

题目描述

给定一个长度为 n n n 的数列 a a a,初始情况下 a i = i a_i=i ai=i

另有一个取值在 [ 1 , n ] [1,n] [1,n] 内的随机的整数 x x x,它取 i i i 的概率为 b i b_i bi

接下来进行 k k k 次操作,每次均匀随机地选两个 [ 1 , n ] [1,n] [1,n] 中的整数 i , j i,j i,j(允许 i = j i=j i=j),交换 a i , a j a_i,a_j ai,aj 的值(如果 i = j i=j i=j 则什么也不干)。问最后 x x x 在位置 i i i 上的概率,你需要对所有 1 ≤ i ≤ n 1\leq i\leq n 1in 求出答案。你需要输出答案模 3221225473 3221225473 3221225473 的值。

我们定义 x x x 在位置 i i i 上指 a i = x a_i=x ai=x

输入格式

一行三个整数 n , k , s e e d n,k,seed n,k,seed。接下来使用如下代码生成 b i b_i bi

#include <cstdio>

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;

const uint mod = 3221225473u;
const int N = 20000010;

ull seed;

ull getnext() {
	seed ^= seed << 13;
	seed ^= seed >> 17;
	seed ^= seed << 5;
	return seed;
}

uint rd(uint l, uint r) {
	return getnext() % (r - l + 1) + l;
}

int n;
ull k;
uint b[N];

int main() {
	scanf("%d%llu%llu", &n, &k, &seed);
	ull sum = 0;
	for (int i = 1; i < n; ++ i) b[i] = rd(2u, mod - 1), (sum += b[i]) %= mod;
	b[n] = mod + 1 - sum;
}

输出格式

a n s i ans_i ansi 表示 x x x 在位置 i i i 上的概率模 3221225473 3221225473 3221225473,则输出 a n s i × i ans_i\times i ansi×i 的异或和。

输入输出样例 #1

输入 #1

2 9 998244353

输出 #1

2684354563

输入输出样例 #2

输入 #2

7 3 123456789

输出 #2

24313281849

输入输出样例 #3

输入 #3

10 9000000000000000000 1000000000000000000

输出 #3

20026214895

输入输出样例 #4

输入 #4

4 0 123456789

输出 #4

12357556560

说明/提示

【样例解释】

对于样例 #1:

b b b 数组为 { 2134949164 , 1086276310 } \{2134949164 ,1086276310\} {2134949164,1086276310},操作 9 9 9 次后 x x x 在两个位置的概率均为 1 2 \dfrac12 21

对于样例 #2:

b b b 数组为 { 1863763622 , 1043615898 , 1055155266 , 1556793106 , 1763540175 , 1239801170 , 1141007183 } \{1863763622,1043615898,1055155266,1556793106,1763540175,1239801170,1141007183\} {1863763622,1043615898,1055155266,1556793106,1763540175,1239801170,1141007183}

【数据范围】

对于 100 % 100\% 100% 的数据:

  • 2 ≤ n ≤ 2 × 1 0 7 2\leq n\leq2\times10^7 2n2×107 0 ≤ k , s e e d < 2 64 0\leq k,seed<2^{64} 0k,seed<264
  • 1 < b i < 3221225473 1<b_i<3221225473 1<bi<3221225473 ∑ i = 1 n b i ≡ 1 ( m o d 3221225473 ) \sum\limits_{i=1}^n b_i\equiv 1\pmod{3221225473} i=1nbi1(mod3221225473)
  • 数据保证 1 < b n < 3221225473 1<b_n<3221225473 1<bn<3221225473 3221225473 3221225473 3221225473 是质数。

本题采用捆绑测试

Subtask \text{Subtask} Subtask n ≤ n\le n k ≤ k\le k分值
0 0 0 2 2 2 2 64 − 1 2^{64}-1 2641 1 1 1
1 1 1 5 5 5 5 5 5 4 4 4
2 2 2 200 200 200 200 200 200 6 6 6
3 3 3 200 200 200 2 64 − 1 2^{64}-1 2641 9 9 9
4 4 4 2000 2000 2000 2000 2000 2000 7 7 7
5 5 5 2 × 1 0 7 2\times10^7 2×107 1 1 1 5 5 5
6 6 6 1 0 6 10^6 106 1 0 6 10^6 106 8 8 8
7 7 7 2 × 1 0 7 2\times10^7 2×107 1 0 7 10^7 107 10 10 10
8 8 8 1 0 6 10^6 106 2 64 − 1 2^{64}-1 2641 15 15 15
9 9 9 2 × 1 0 7 2\times10^7 2×107 2 64 − 1 2^{64}-1 2641 35 35 35

P8944 矩阵快速幂

a n s d ans_d ansd,令x=c。
性质一:任意时刻,只需要考虑两种状态a[d]是否等于c。用数学归纳法证明。操作K次后,a[d]等于c符合题意,不等于不符合题意。操作K-1次后哦,如果前置状态a[d]等于c,后序状态分三种情况:
一,i和j都等于d,状态不变。概率p1 = n-2
二,i和j都不等于d,状态不变。概率p2=(n-1)2n-2
三,i,j中的一个d,另外一个不等于。p3 = 1-p1-p2。
如果前置状态a[d]不等于c。则分两情况:
一,i = d ,a[j] =c ,或ij互换。状态改变,概率p4 = 2p1。
二,除情况一外,状态不会变化。概率p5=1-p4。
整个转移过程c和d的具体值无关。故:
mat = {{p1+p2},p3},{p4,p5}}
ans = pre × \times × matK
对于任意c、d 转移矩阵是一样的matK,但初始值不一样:
pre = {b[i],0}。
按题意组装答案就可以了。
注意:本题只封装了prematn,没有封装matk,故用单元矩阵matn代替。
pre*matK,因为只需要计算 ans[0][0],故无需运算整个矩阵。手动计算ans[0][0],否则超时。

代码

核心代码

#include <iostream>
#include <sstream>
#include <vector>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<string>
#include<algorithm>
#include<functional>
#include<queue>
#include <stack>
#include<iomanip>
#include<numeric>
#include <math.h>
#include <climits>
#include<assert.h>
#include<cstring>
#include<list>

#include <bitset>
using namespace std;

template<class T1, class T2>
std::istream& operator >> (std::istream& in, pair<T1, T2>& pr) {
	in >> pr.first >> pr.second;
	return in;
}

template<class T1, class T2, class T3 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t);
	return in;
}

template<class T1, class T2, class T3, class T4 >
std::istream& operator >> (std::istream& in, tuple<T1, T2, T3, T4>& t) {
	in >> get<0>(t) >> get<1>(t) >> get<2>(t) >> get<3>(t);
	return in;
}

template<class T = int>
vector<T> Read() {
	int n;
	scanf("%d", &n);
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<class T = int>
vector<T> Read(int n) {
	vector<T> ret(n);
	for (int i = 0; i < n; i++) {
		cin >> ret[i];
	}
	return ret;
}

template<int N = 1'000'000>
class COutBuff
{
public:
	COutBuff() {
		m_p = puffer;
	}
	template<class T>
	void write(T x) {
		int num[28], sp = 0;
		if (x < 0)
			*m_p++ = '-', x = -x;

		if (!x)
			*m_p++ = 48;

		while (x)
			num[++sp] = x % 10, x /= 10;

		while (sp)
			*m_p++ = num[sp--] + 48;
		AuotToFile();
	}
	void writestr(const char* sz) {
		strcpy(m_p, sz);
		m_p += strlen(sz);
		AuotToFile();
	}
	inline void write(char ch)
	{
		*m_p++ = ch;
		AuotToFile();
	}
	inline void ToFile() {
		fwrite(puffer, 1, m_p - puffer, stdout);
		m_p = puffer;
	}
	~COutBuff() {
		ToFile();
	}
private:
	inline void AuotToFile() {
		if (m_p - puffer > N - 100) {
			ToFile();
		}
	}
	char  puffer[N], * m_p;
};

template<int N = 1'000'000>
class CInBuff
{
public:
	inline CInBuff() {}
	inline CInBuff<N>& operator>>(char& ch) {
		FileToBuf();
		ch = *S++;
		return *this;
	}
	inline CInBuff<N>& operator>>(int& val) {
		FileToBuf();
		int x(0), f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行		
		return *this;
	}
	inline CInBuff& operator>>(long long& val) {
		FileToBuf();
		long long x(0); int f(0);
		while (!isdigit(*S))
			f |= (*S++ == '-');
		while (isdigit(*S))
			x = (x << 1) + (x << 3) + (*S++ ^ 48);
		val = f ? -x : x; S++;//忽略空格换行
		return *this;
	}
	template<class T1, class T2>
	inline CInBuff& operator>>(pair<T1, T2>& val) {
		*this >> val.first >> val.second;
		return *this;
	}
	template<class T1, class T2, class T3>
	inline CInBuff& operator>>(tuple<T1, T2, T3>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val);
		return *this;
	}
	template<class T1, class T2, class T3, class T4>
	inline CInBuff& operator>>(tuple<T1, T2, T3, T4>& val) {
		*this >> get<0>(val) >> get<1>(val) >> get<2>(val) >> get<3>(val);
		return *this;
	}
	template<class T = int>
	inline CInBuff& operator>>(vector<T>& val) {
		int n;
		*this >> n;
		val.resize(n);
		for (int i = 0; i < n; i++) {
			*this >> val[i];
		}
		return *this;
	}
	template<class T = int>
	vector<T> Read(int n) {
		vector<T> ret(n);
		for (int i = 0; i < n; i++) {
			*this >> ret[i];
		}
		return ret;
	}
	template<class T = int>
	vector<T> Read() {
		vector<T> ret;
		*this >> ret;
		return ret;
	}
private:
	inline void FileToBuf() {
		const int canRead = m_iWritePos - (S - buffer);
		if (canRead >= 100) { return; }
		if (m_bFinish) { return; }
		for (int i = 0; i < canRead; i++)
		{
			buffer[i] = S[i];//memcpy出错			
		}
		m_iWritePos = canRead;
		buffer[m_iWritePos] = 0;
		S = buffer;
		int readCnt = fread(buffer + m_iWritePos, 1, N - m_iWritePos, stdin);
		if (readCnt <= 0) { m_bFinish = true; return; }
		m_iWritePos += readCnt;
		buffer[m_iWritePos] = 0;
		S = buffer;
	}
	int m_iWritePos = 0; bool m_bFinish = false;
	char buffer[N + 10], * S = buffer;
};

template<class T = long long>
class CMatMul
{
public:
	CMatMul(T llMod = 1e9 + 7) :m_llMod(llMod) {}
	// 矩阵乘法
	vector<vector<T>> multiply(const vector<vector<T>>& a, const vector<vector<T>>& b) {
		const int r = a.size(), c = b.front().size(), iK = a.front().size();
		assert(iK == b.size());
		vector<vector<T>> ret(r, vector<T>(c));
		for (int i = 0; i < r; i++)
		{
			for (int j = 0; j < c; j++)
			{
				for (int k = 0; k < iK; k++)
				{
					ret[i][j] = (ret[i][j] + a[i][k] * b[k][j]) % m_llMod;
				}
			}
		}
		return ret;
	}

	// 矩阵快速幂
	vector<vector<T>> pow(const vector<vector<T>>& a, vector<vector<T>> b, T n) {
		vector<vector<T>> res = a;
		for (; n; n /= 2) {
			if (n % 2) {
				res = multiply(res, b);
			}
			b = multiply(b, b);
		}
		return res;
	}
	vector<vector<T>> TotalRow(const vector<vector<T>>& a)
	{
		vector<vector<T>> b(a.front().size(), vector<T>(1, 1));
		return multiply(a, b);
	}
protected:
	const  T m_llMod;
};

typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;

template<class T1 = int, class T2 = long long, T1 MOD = 1000000007>
class C1097Int
{
public:
	C1097Int(T1 iData = 0) :m_iData(iData% MOD)
	{

	}
	C1097Int(T2 llData) :m_iData(llData% MOD) {

	}
	C1097Int  operator+(const C1097Int& o)const
	{
		return C1097Int(((T2)m_iData + o.m_iData) % MOD);
	}
	C1097Int& operator+=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData + o.m_iData) % MOD;
		return *this;
	}
	C1097Int& operator-=(const C1097Int& o)
	{
		m_iData = ((T2)MOD + m_iData - o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator-(const C1097Int& o)
	{
		return C1097Int(((T2)MOD + m_iData - o.m_iData) % MOD);
	}
	C1097Int  operator*(const C1097Int& o)const
	{
		return((T2)m_iData * o.m_iData) % MOD;
	}
	C1097Int& operator*=(const C1097Int& o)
	{
		m_iData = ((T2)m_iData * o.m_iData) % MOD;
		return *this;
	}
	C1097Int  operator/(const C1097Int& o)const
	{
		return *this * o.PowNegative1();
	}
	C1097Int& operator/=(const C1097Int& o)
	{
		*this /= o.PowNegative1();
		return *this;
	}
	bool operator==(const C1097Int& o)const
	{
		return m_iData == o.m_iData;
	}
	bool operator<(const C1097Int& o)const
	{
		return m_iData < o.m_iData;
	}
	C1097Int pow(T2 n)const
	{
		C1097Int iRet = (T1)1, iCur = *this;
		while (n)
		{
			if (n & 1)
			{
				iRet *= iCur;
			}
			iCur *= iCur;
			n >>= 1;
		}
		return iRet;
	}
	C1097Int PowNegative1()const
	{
		return pow(MOD - 2);
	}
	T1 ToInt()const
	{
		return ((T2)m_iData + MOD) % MOD;
	}
private:
	T1 m_iData = 0;;
};
typedef C1097Int<uint, ull, 3221225473u> BI;
class Solution {
public:
	const uint MOD = 3221225473u;
	ull m_seed;
	ull getnext() {
		m_seed ^= m_seed << 13;
		m_seed ^= m_seed >> 17;
		m_seed ^= m_seed << 5;
		return m_seed;
	}
	uint rd(uint l, uint r) {
		return getnext() % (r - l + 1) + l;
	}
	ull Ans(uint n, ull K, ull seed) {
		m_seed = seed;
		ull sum = 0;
		vector<uint> b(n);
		for (int i = 0; i + 1 < n; ++i) b[i] = rd(2u, MOD - 1), (sum += b[i]) %= MOD;
		b.back() = MOD + 1 - sum;
		BI n1 = BI(n).PowNegative1();//1/n
		BI p1 = n1 * n1;
		BI p2 = BI(n - 1) * BI(n - 1) * n1 * n1;
		BI p3 = BI((uint)1) - (p1 + p2);
		BI p4 = p1 * BI(2u);
		BI p5 = BI(1u) - p4;
		vector<vector<ull>> mat = { {(p1 + p2).ToInt(),p3.ToInt() }, { p4.ToInt(),p5.ToInt() } };
		vector<vector<ull>> unit = { {1,0},{0,1} };
		CMatMul<ull> matMul(MOD);
		auto matK = matMul.pow(unit, mat, K);
		ull ans = 0;
		const ull c0 = matK[0][0], c1 = matK[1][0];
		for (int i = 1; i <= n; i++) {
			const ull r0 = b[i - 1], r1 = 1u + MOD - r0;
			ull curAns = (r0 * c0) % MOD + (r1 * c1) % MOD;
			if (curAns > MOD) { curAns -= MOD; }
			ans ^= (i * curAns);
		}
		return ans;
	}
};

int main() {
#ifdef _DEBUG
	freopen("a.in", "r", stdin);
#endif // DEBUG	
	ios::sync_with_stdio(0);
	uint n;
	ull k, seek;
	cin >> n >> k >> seek;
#ifdef _DEBUG		
	//printf("T=%lld,", T);
	//Out(ks, "ks=");
	//Out(edge, ",edge=");
	/*Out(edge, "edge=");
	Out(que, "que=");*/
#endif // DEBUG	
	auto res = Solution().Ans(n,k,seek);
	cout << res;
	return 0;
}

单元测试

vector<vector<vector<long long>>> mats;
		vector<pair<int, int>> que;
		TEST_METHOD(TestMethod1)
		{		
			auto res = Solution().Ans(2, 9, 998244353);
			AssertEx((ull)2684354563u, res);
		}
		TEST_METHOD(TestMethod2)
		{
			auto res = Solution().Ans(7, 3 ,123456789);
			AssertEx((ull)24313281849u, res);
		}
		TEST_METHOD(TestMethod3)
		{
			auto res = Solution().Ans(10, 9000000000000000000 ,1000000000000000000);
			AssertEx((ull)20026214895u, res);
		}
		TEST_METHOD(TestMethod4)
		{
			auto res = Solution().Ans(4, 0 ,123456789);
			AssertEx((ull)12357556560u, res);
		}

扩展阅读

我想对大家说的话
工作中遇到的问题,可以按类别查阅鄙人的算法文章,请点击《算法与数据汇总》。
学习算法:按章节学习《喜缺全书算法册》,大量的题目和测试用例,打包下载。重视操作
有效学习:明确的目标 及时的反馈 拉伸区(难度合适) 专注
闻缺陷则喜(喜缺)是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。
如果程序是一条龙,那算法就是他的是睛
失败+反思=成功 成功+反思=成功

视频课程

先学简单的课程,请移步优快云学院,听白银讲师(也就是鄙人)的讲解。
https://edu.youkuaiyun.com/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.youkuaiyun.com/lecturer/6176

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

软件架构师何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值