本文涉及知识点
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 1≤i≤n 求出答案。你需要输出答案模 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 2≤n≤2×107, 0 ≤ k , s e e d < 2 64 0\leq k,seed<2^{64} 0≤k,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=1∑nbi≡1(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 264−1 | 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 264−1 | 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 264−1 | 15 15 15 |
9 9 9 | 2 × 1 0 7 2\times10^7 2×107 | 2 64 − 1 2^{64}-1 264−1 | 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++**实现。