【洛谷5472】[NOI2019] 斗主地(猜结论)
- 有一个\(n\)张牌的牌堆,初始从上往下依次编号\(1\sim n\),编号为\(i\)的牌的权值为\(x^{ty}\)。
- 有\(m\)轮洗牌,第\(i\)次会先将牌堆最上面\(a_i\)张牌取出另成一堆。假设当前两堆分别剩余\(X,Y\)张牌,则会以\(\frac X{X+Y}\)的概率取出第一堆最下面的牌,或以\(\frac Y{X+Y}\)的概率取出第二堆最下面的牌,放在新牌堆的最上面。
- \(q\)次询问,每次询问最后的第\(i\)张牌权值的期望。
- \(n\le10^7,m\le5\times10^5,ty=1\ or\ 2\)
非常玄学的猜结论题
这道题的核心结论:一次函数洗牌之后的期望还是一次函数,二次函数洗牌后的期望还是二次函数。
证明暂且不会,先坑了。(话说如果真在考场上遇到这种题目,真不知道自己有没有猜结论并相信自己的结论的自信。。。)
如果这个结论是正确的,那么\(m\)次洗牌之后得到的仍应是一次函数/二次函数,也就是说我们只要任选三个好算的位置算一算就好了。
首先第一个位置和最后一个位置肯定是很好算的,那么我们再添上第二个位置就凑齐三个位置了。
转移很显然:
\[x_1=x_1\times \frac an+x_{a+1}\times\frac {n-a}n\\
x_n=x_a\times\frac an+x_n\times\frac{n-a}n\\
x_2=x_2\times\frac{a-1}{n-1}\times\frac an+x_{a+1}\times\frac{n-a}{n-1}\times\frac an+x_{1}\times\frac{a}{n-1}\times\frac{n-a}n+x_{a+2}\times\frac{n-a-1}{n-1}\times\frac{n-a}n
\]
发现分母实际上只有\(n\)和\(\frac1{(n-1)n}\),可以实现预处理避免复杂度平添一个\(log\)。
而要由三个值解出一个二次函数,只要列出三个方程:
\[\begin{cases}
A+B+C=x_1,\\
4A+2B+C=x_2,\\
n^2A+nB+C=x_n
\end{cases}
\Rightarrow
\begin{cases}
A=\frac{(n-2)x_1-(n-1)x_2+x_n}{n^2-3n+2},\\
B=x_2-x_1-3A,\\
C=x_1-A-B
\end{cases}
\]
分母同样可以预处理。
所以说这道题的难点就在于最开始洗牌之后函数次数不变的结论,之后的过程真就超级简单了。
代码:\(O(m+q)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define X 998244353
using namespace std;
int n,m,ty;I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
int A,B,C;I int F(CI x) {return (1LL*A*x%X*x+1LL*B*x+C)%X;}//计算F(x)的值
int IV;I void Calc(CI x,CI y,CI z) {A=(1LL*(n-2)*x%X-1LL*(n-1)*y%X+z+X)*IV%X,B=(y-x-3LL*A+4LL*X)%X,C=(x-A-B+2LL*X)%X;}//解方程
int main()
{
read(n,m,ty);RI iv=QP(n,X-2),iv_=1LL*iv*QP(n-1,X-2)%X;IV=QP((1LL*n*n-3*n+2)%X,X-2);//预处理分母
RI i,a,x=1,y=QP(2,ty),z=QP(n,ty);for(i=1;i<=m;++i) read(a),Calc(x,y,z),//求出二次函数
x=(1LL*F(1)*a+1LL*F(a+1)*(n-a))%X*iv%X,z=(1LL*F(a)*a+1LL*F(n)*(n-a))%X*iv%X,//求出新的x1,xn
y=((1LL*F(2)*(a-1)+1LL*F(a+1)*(n-a))%X*a+(1LL*F(1)*a+1LL*F(a+2)*(n-a-1))%X*(n-a))%X*iv_%X;//求出新的x2
RI Qt;read(Qt),Calc(x,y,z);W(Qt--) read(a),writeln(F(a));return clear(),0;//代入函数中计算值
}
待到再迷茫时回头望,所有脚印会发出光芒