1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alarm5854
Stop Smoking!搬运于
2025-08-24 22:40:22,当前版本为作者最后更新于2021-09-03 21:51:26,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
来自本题出题人官方题解,过了近两个月才想起来发,写得很丑,赛时果然被爆标了。
题意
一开始有一个带 个点的环,从 到 顺时针排列。
设上一个点返回的状态为 ,则:
- 若 ,则这个环上第 个点有 的概率返回 且删除这个点,有 的概率返回 但保留这个点,有 的概率返回 且保留这个点。
- 若 ,则这个环上第 个点有 的概率返回 且删除这个点,有 的概率返回 且保留这个点。
初始 ,从 号点开始进行上述操作,操作完后找到沿顺时针方向下一个点,这个点成为下一个操作的点。
求只剩一个点的期望操作次数,若期望次数为无穷大,输出 ,否则输出这个概率对 取模的值,可以证明答案为有理数。
前置知识
期望,状压,高斯消元。
子任务
很明显,一开始就只有一个点,期望改变状态次数为 。
期望得分 分。
子任务
这时每个点肯定会返回 且被保留,因此当 时期望次数为无穷大, 时期望次数为 。
期望得分 分(这里的期望得分均为结合前面的子任务的得分)。
子任务
这时每个点肯定会返回 且被删除,因此到 号点的时候就只剩一个点了,答案即为 。
期望得分 分。
子任务
这时每个点肯定会返回 且被保留,因此情况同子任务 。
期望得分 分。
子任务
这时 号点一直返回 ,设这是第 圈,则 号点会返回 并被删除, 会返回 并被保留。第 圈的操作次数为 ,第 圈不用操作,故答案为 。
期望得分 分。
子任务
这时每个点都本质相同,且没有任何点返回 ,设 为剩 个点时的期望次数,则
得出
期望得分 分。
子任务
这时每个点仍然本质相同,设 为剩 个点,且 的期望次数, 为剩 个点,且 的期望次数。
那么有
得出
期望得分 分。
子任务
其实就是子任务 的拓展版本,按照前面所说,则
$$f(1)=0,f(n)=\dfrac{a_i}{1000}f(n-1)+\dfrac{b_i}{1000}f(n)+\dfrac{1000-a_i-b_i}{1000}g(n)+1 $$$$g(n)=\dfrac{a_i+b_i}{1000}f(n-1)+\dfrac{1000-a_i-b_i}{1000}g(n)+1 $$解得
$$f(n)=\dfrac{1000\times (n-1)}{(a_i+b_i)\times(1-b_i)} $$期望得分 分。
子任务
进入正题,由于 ,于是可以想到状压。
从这里开始 表示前面的 。
设 表示剩余的点的状态为 ,现在在这个状态中第 个为 的地方 (0下标) ,上一个点的返回值为 的期望局数, 和 的区别在于变成上一个点返回值为 的期望局数,则有:
这就是题目中的定义, 的转移为:
$$\begin{aligned} f(S,i)&=\dfrac{a_{S_i}}{1000}f(S\oplus 2^{S_i},i\bmod (|S|-1))\\&+\dfrac{b_{S_i}}{1000}f(S,(i+1)\bmod |S|)\\&+\dfrac{1000-a_{S_i}-b_{S_i}}{1000}g(S,(i+1)\bmod |S|) \end{aligned} $$$$\begin{aligned} g(S,i)&=\dfrac{a_{S_i}+b_{S_i}}{1000}f(S\oplus 2^{S_i},i\bmod (|S|-1))\\&+\dfrac{1000-a_{S_i}-b_{S_i}}{1000}g(S,(i+1)\bmod |S|) \end{aligned} $$这样,可以考虑对于每一个 ,都列出这样的 元的线性方程,可以用高斯消元解决。
至于怎么高斯消元,需要对上述的式子进行变形:
$$\begin{aligned} f(S,i)&-\dfrac{b_{S_i}}{1000}f(S,(i+1)\bmod |S|)-\dfrac{1-a_{S_i}-b_{S_i}}{1000}g(S,(i+1)\bmod |S|)\\&=\dfrac{a_{S_i}}{1000}f(S\oplus 2^{S_i},i\bmod (|S|-1)) \end{aligned} $$$$\begin{aligned} g(S,i)&-\dfrac{1000-a_{S_i}-b_{S_i}}{1000}g(S,(i+1)\bmod |S|)\\&=\dfrac{a_{S_i}+b_{S_i}}{1000}g(S\oplus 2^{S_i},i\bmod (|S|-1)) \end{aligned} $$这样就能够把高斯消元矩阵构造出来了,大小为 。
尤其需要注意的是如果出现了除以 的情况,一定要把这个答案弄成一个不在 范围内的数并进行特判,否则会出问题。
朴素的高斯消元复杂度为 ,快速幂的复杂度为 ,其中 与 同阶,故时间复杂度为 (实际上是 )。
期望得分 分。
子任务
由于 ,所以考虑预处理逆元,这样可以将复杂度降至 。
期望得分 分。
子任务
发现这个矩阵是个稀疏矩阵,每列非 的元素数量很少,对于一列为 的,就不需要进行减法,因为这是无效的,这样复杂度可以降至 。
期望得分 分。
标程
//去掉注释后长度:2053字节 #include<ctime> #include<cstdio> #include<cctype> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll p=1e6+33; const ll N=18; int read(){ char c; int x=0,f=1; while(!isdigit(c=getchar())) f-=2*(c=='-'); while(isdigit(c)){ x=x*10+f*(c-48); c=getchar(); } return x; } ll n,t,k,a[21],b[21],f[1<<N][21]; ll l[1<<N],g[1<<N],c[37][37],s[37]; ll inv[p]; void gauss(ll n){ for(ll i=0;i<n;++i){ ll t=i; for(ll j=i;j<n;++j){ if(c[j][i]){ t=j; break; } } if(!c[t][i]) continue; swap(c[i],c[t]); for(ll j=i+1;j<n;++j){ if(!c[j][i])//优化 continue; ll x=c[j][i]*inv[c[i][i]]; for(ll k=i;k<=n;++k){ if(~c[j][k]) c[j][k]=(c[j][k]-x*c[i][k]%p+p)%p; } } } for(ll i=n-1;~i;--i){ if(!c[i][i]||!~c[i][n]) s[i]=-1;//这里的-1即为特殊值,需要特判 else s[i]=c[i][n]*inv[c[i][i]]%p; if(!i) continue; for(ll j=i-1;~j;--j){ if(~c[j][n]&&(~s[i]||!c[j][i]))//仍然需要特判-1 c[j][n]=(c[j][n]-c[j][i]*s[i]%p+p)%p; else c[j][n]=-1; c[j][i]=0; } } } int main(){ n=read(); t=read(); inv[1]=1; k=1<<n; for(ll i=2;i<p;++i) inv[i]=(p-p/i)*inv[p%i]%p;//线性处理逆元 for(ll i=0;i<n;++i){ a[i]=read()*inv[1000]%p;//提前进行转化 b[i]=read()*inv[1000]%p; } for(ll i=1;i<k;++i) l[i]=l[i^(i&-i)]+1; for(ll i=0;i<n;++i) g[1<<i]=i; for(ll i=1;i<k;++i){ if(l[i]==1) continue; memset(c,0,sizeof(c));//初始清空矩阵 for(ll j=0,t=i;t;t^=t&-t,++j){ ll x=g[t&-t]; c[j*2][j*2]=1; c[j*2+1][j*2+1]=1; c[j*2][(j*2+2)%(l[i]*2)]=(p-b[x]%p)%p; c[j*2][(j*2+3)%(l[i]*2)]=(a[x]+b[x]-1+p)%p;//由于一开始进行了处理,所以就不再是a[x]+b[x]-1000了 c[j*2+1][(j*2+3)%(l[i]*2)]=(a[x]+b[x]-1+p)%p; if(~f[i^(1<<x)][j%(l[i]-1)]||!a[x]) c[j*2][l[i]*2]=(a[x]*f[i^(1<<x)][j%(l[i]-1)]+1)%p; else c[j*2][l[i]*2]=-1; if(~f[i^(1<<x)][j%(l[i]-1)]||!(a[x]+b[x])) c[j*2+1][l[i]*2]=((a[x]+b[x])*f[i^(1<<x)][j%(l[i]-1)]+1)%p; else c[j*2+1][l[i]*2]=-1; //c[j*2]为f所对应的位置,c[j*2+1]为g所对应的位置 } gauss(l[i]*2); for(int j=0;j<l[i];++j) f[i][j]=s[j*2]; } printf("%lld\n",f[k-1][0]); return 0; }
- 1
信息
- ID
- 7123
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者