1 条题解

  • 0
    @ 2025-8-24 22:40:22

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Alarm5854
    Stop Smoking!

    搬运于2025-08-24 22:40:22,当前版本为作者最后更新于2021-09-03 21:51:26,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    来自本题出题人官方题解,过了近两个月才想起来发,写得很丑,赛时果然被爆标了。

    题意

    一开始有一个带 nn 个点的环,从 11nn 顺时针排列。

    设上一个点返回的状态为 xx,则:

    • x=1x=1,则这个环上第 ii 个点有 ai1000\dfrac{a_i}{1000} 的概率返回 11 且删除这个点,有 bi1000\dfrac{b_i}{1000} 的概率返回 11 但保留这个点,有 1000aibi1000\dfrac{1000-a_i-b_i}{1000} 的概率返回 22 且保留这个点。
    • x=2x=2,则这个环上第 ii 个点有 ai+bi1000\dfrac{a_i+b_i}{1000} 的概率返回 11 且删除这个点,有 1000aibi1000\dfrac{1000-a_i-b_i}{1000} 的概率返回 22 且保留这个点。

    初始 x=1x=1,从 11 号点开始进行上述操作,操作完后找到沿顺时针方向下一个点,这个点成为下一个操作的点。

    求只剩一个点的期望操作次数,若期望次数为无穷大,输出 1-1,否则输出这个概率对 106+3310^6+33 取模的值,可以证明答案为有理数。

    前置知识

    期望,状压,高斯消元。

    子任务 11

    很明显,一开始就只有一个点,期望改变状态次数为 00

    期望得分 22 分。

    子任务 22

    这时每个点肯定会返回 22 且被保留,因此当 n>1n>1 时期望次数为无穷大,n=1n=1 时期望次数为 00

    期望得分 44 分(这里的期望得分均为结合前面的子任务的得分)。

    子任务 33

    这时每个点肯定会返回 11 且被删除,因此到 nn 号点的时候就只剩一个点了,答案即为 n1n-1

    期望得分 88 分。

    子任务 44

    这时每个点肯定会返回 11 且被保留,因此情况同子任务 22

    期望得分 1212 分。

    子任务 55

    这时 11 号点一直返回 22,设这是第 ii 圈,则 i+1i+1 号点会返回 11 并被删除,i+2ni+2\sim n 会返回 11 并被保留。第 i(i<n)i(i<n) 圈的操作次数为 ni+1n-i+1,第 nn 圈不用操作,故答案为 n(n+1)21\dfrac{n(n+1)}{2}-1

    期望得分 1818 分。

    子任务 66

    这时每个点都本质相同,且没有任何点返回 22,设 f(n)f(n) 为剩 nn 个点时的期望次数,则

    f(1)=0,f(n)=12(f(n1)+f(n))+1f(1)=0,f(n)=\dfrac{1}{2}(f(n-1)+f(n))+1

    得出

    f(n)=2(n1)f(n)=2(n-1)

    期望得分 2424 分。

    子任务 77

    这时每个点仍然本质相同,设 f(n)f(n) 为剩 nn 个点,且 x=1x=1 的期望次数,g(n)g(n) 为剩 nn 个点,且 x=2x=2 的期望次数。

    那么有

    f(1)=0,f(n)=12(f(n)+g(n))+1f(1)=0,f(n)=\dfrac{1}{2}(f(n)+g(n))+1 g(n)=12(f(n1)+g(n))+1g(n)=\dfrac{1}{2}(f(n-1)+g(n))+1

    得出

    f(n)=4(n1)f(n)=4(n-1)

    期望得分 3030 分。

    子任务 88

    其实就是子任务 6,76,7 的拓展版本,按照前面所说,则

    $$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)} $$

    期望得分 4949 分。

    子任务 99

    进入正题,由于 n11n\le11,于是可以想到状压。

    从这里开始 aia_i 表示前面的 ai+1a_{i+1}

    f(S,i)f(S,i) 表示剩余的点的状态为 SS,现在在这个状态中第 ii 个为 11 的地方 (0下标) ,上一个点的返回值为 11 的期望局数,g(S,i)g(S,i)f(S,i)f(S,i) 的区别在于变成上一个点返回值为 22 的期望局数,则有:

    f(2k,x)=0(kN,xk)f(2^k,x)=0(k\in \mathbb{N},x\le k)

    这就是题目中的定义,f(S,i)f(S,i) 的转移为:

    $$\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} $$

    这样,可以考虑对于每一个 S[1,2n),S2k,kNS\in[1,2^n),S\neq 2^k,k\in\mathbb{N},都列出这样的 2×S2\times |S| 元的线性方程,可以用高斯消元解决。

    至于怎么高斯消元,需要对上述的式子进行变形:

    $$\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} $$

    这样就能够把高斯消元矩阵构造出来了,大小为 2n×(2n+1)2n\times(2n+1)

    尤其需要注意的是如果出现了除以 00 的情况,一定要把这个答案弄成一个不在 [0,p)[0,p) 范围内的数并进行特判,否则会出问题。

    朴素的高斯消元复杂度为 O(n3)O(n^3),快速幂的复杂度为 O(logp)O(\log p),其中 logp\log pnn 同阶,故时间复杂度为 O(2n×n4)O(2^n\times n^4)(实际上是 O(2n×n3×logp)O(2^n\times n^3\times \log p))。

    期望得分 7575 分。

    子任务 1010

    由于 p=106+33p=10^6+33,所以考虑预处理逆元,这样可以将复杂度降至 O(2n×n3)O(2^n\times n^3)

    期望得分 8383 分。

    子任务 1111

    发现这个矩阵是个稀疏矩阵,每列非 00 的元素数量很少,对于一列为 00 的,就不需要进行减法,因为这是无效的,这样复杂度可以降至 O(2n×n2)O(2^n\times n^2)

    期望得分 100100 分。

    标程

    //去掉注释后长度: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
    上传者