Examples

2022-7-3 #7 AGC044E & CFgym102798B & CFgym102956B & CFgym102979E

虽然期末考只有 \(3\) 天了,但是还是忍不住 OI 了。

019 AGC044E Random Pawn

\(f_i\)\(i\) 位置开始能得到的最大权值,那么有:

\[f_i=\max(a_i,\frac{f_{i-1}+f_{i+1}}{2}-b_i) \]

如果 \(b_i\) 均为零,那么 \(f_i\geqslant\frac{f_{i-1}+f_{i+1}}{2}\),这实际上是对 \((i,a_i)\) 做一个凸壳。由于是环,我们不妨在 \(\max a\) 处断环为链。

我们想把 \(b_i\) 消除,不妨使用一组偏移量 \(c_i\) 使得 \(f_i\leftarrow f_i+c_i\),可解得:

\[c_i=\frac{c_{i-1}+c_{i+1}}{2}-b_i \]

\(c_1=c_2=0\) 可直接递推,最后找个凸壳就好了,复杂度 \(O(n)\)

#include<stdio.h>
const int maxn=200005;
int n,pos,top;
long long a[maxn],c[maxn],ta[maxn];
int b[maxn],tb[maxn],stk[maxn];
long double ans;
struct vec{
	long long x,y;
	inline vec operator -(const vec&p){
		return vec{x-p.x,y-p.y};
	}
	inline long long operator ^(const vec&p){
		return x*p.y-p.x*y;
	}
}p[maxn];
inline int anticlock(vec a,vec b,vec c){
	return ((b-a)^(c-a))>0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&ta[i]);
		if(pos==0||ta[i]>ta[pos])
			pos=i;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&tb[i]);
	for(int i=1;i<=n+1;i++)
		a[i]=ta[(pos+i-2)%n+1],b[i]=tb[(pos+i-2)%n+1];
	n++;
	for(int i=3;i<=n;i++)
		c[i]=2*b[i-1]+2*c[i-1]-c[i-2];
	for(int i=1;i<=n;i++){
		a[i]-=c[i],p[i]=vec{a[i],0ll+i};
		if(i<n)
			ans+=c[i];
	}
	stk[++top]=1,stk[++top]=2;
	for(int i=3;i<=n;i++){
		while(top>=2&&anticlock(p[stk[top-1]],p[stk[top]],p[i])==0)
			top--;
		stk[++top]=i;
	}
	n--;
	for(int i=1,j=1;i<=n;i++){
		while(j<top&&i>=stk[j])
			j++;
		ans+=1.0L*(a[stk[j-1]]*(stk[j]-i)+a[stk[j]]*(i-stk[j-1]))/(stk[j]-stk[j-1]);
	}
	printf("%.10Lf\n",ans/n);
	return 0;
}

020 CFgym102798E So Many Possibilities...

比较简单的题。

看到数据范围就应该想到状压零集合 \(S\),令 \(f_{i,S}\) 表示走了 \(i\) 步,目前零集合为 \(S\) 的概率。

由于不能记录每个位置剩余的值,我们不妨钦定这次转移将哪个位置变成 \(0\) 并一次性分配其概率。(也可以无事发生)

虽说除去已经分配的操作,每个数被某次操作分配到的概率都是均等的,但是不同阶段的概率还是不同,于是我们需要提前把这个概率放到 dp 状态里,也就是每一步都钦定了一个位置放。

最后还会剩下一些操作,再用一个 dp 统计一下。令 \(g_{i,S}\) 为对集合 \(S\) 操作 \(i\) 步后没有一个零的方案数,转移显然。

复杂度 \(O((n+m)m2^n)\),可以通过。

#include<stdio.h>
const int maxn=20,maxm=105,maxs=1<<15;
int n,m,S;
int a[maxn],sum[maxs];
double ans;
double f[maxm][maxs],C[maxm][maxm],g[maxm][maxs];
int main(){
	for(int i=0;i<=100;i++){
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++)
			C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
	scanf("%d%d",&n,&m),S=(1<<n)-1;
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	f[0][0]=1;
	for(int i=0;i<=S;i++){
		g[0][i]=1;
		if(i){
			int j=__builtin_ctz(i);
			sum[i]=sum[i^(1<<j)]+a[j+1];
		}
	}
	for(int i=1;i<=m;i++)
		for(int j=0;j<=S;j++){
			if(j){
				for(int k=1;k<=n;k++)
					if((j>>(k-1))&1){
						int lst=j^(1<<(k-1));
						if(i-1>=sum[lst])
							f[i][j]+=f[i-1][lst]*C[i-1-sum[lst]][a[k]-1];
					}
				int l=__builtin_ctz(j);
				for(int k=0;k<a[l+1]&&k<=i;k++)
					g[i][j]+=g[i-k][j^(1<<l)]*C[i][k];
				f[i][j]/=(n-__builtin_popcount(j)+1);
			}
			if(__builtin_popcount(j)<n)
				f[i][j]+=f[i-1][j]/(n-__builtin_popcount(j));
		}
	for(int i=0;i<=S;i++)
		if(m>=sum[i])
			ans+=__builtin_popcount(i)*f[m][i]*g[m-sum[i]][S^i];
	printf("%.6lf\n",ans);
	return 0;
}

021 CFgym102956B Beautiful Sequence Unraveling

和上一道题存在相似之处,不过还简单一点。

\(f_{i,j}\) 为值域 \([1,i]\),长度为 \(j\) 的序列,值域不一定填满,最大是 \(i\) 的方案数,我们将其 dp 出来后再容斥出一个 \(g_i\) 表示离散化后值域为 \([1,i]\) 的方案数就好了。

\(f\) 的转移可以考虑找到最后一个不合法的位置 \(k\) 以及其取值 \(v\)\(k\) 之后的是一个 \([v,i]\) 的子问题,而前面就是一个 \(\max=t\) 的任意序列,可以直接算。

\[f_{i,j}=i^j-\sum_{k=1}^{i-1}\sum_{v=1}^j(v^k-(v-1)^k)(f_{i-v+1,j-k}-f_{i-v,j-k}) \]

我们把 \(v^{-?}\) 带到状态里就可以前缀和优化了,复杂度 \(O(n^3)\)

#include<stdio.h>
const int maxn=405;
int n,m,mod,ans;
int inv[maxn],f[maxn][maxn],g[maxn],sum[maxn][maxn][2],mul[maxn][maxn],imul[maxn][maxn];
int C(int a,int b){
	int res=1;
	for(int i=a,j=1;j<=b;i--,j++)
		res=1ll*res*i%mod*inv[j]%mod;
	return res;
}
int main(){
	scanf("%d%d%d",&n,&m,&mod);
	inv[1]=1;
	for(int i=2;i<=n;i++)
		inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
	for(int i=1;i<=n;i++){
		mul[i][0]=imul[i][0]=1;
		for(int j=1;j<=n;j++)
			mul[i][j]=1ll*mul[i][j-1]*i%mod,imul[i][j]=1ll*imul[i][j-1]*inv[i]%mod;
	}
	for(int t=1;t<=n;t++){
		for(int i=1;i<=n;i++)
			sum[0][i][0]=sum[0][i][1]=0;
		for(int i=1;i<=n;i++){
			int inc=mul[t][i],dec=0;
			for(int j=1;j<=t;j++)
				dec=(dec+1ll*mul[j][i]*sum[i-1][j][0])%mod,inc=(inc+1ll*mul[j-1][i]*sum[i-1][j][1])%mod;
			f[t][i]=(inc-dec+mod)%mod;
			for(int j=1;j<=t;j++){
				sum[i][j][0]=(sum[i-1][j][0]+1ll*imul[j][i]*(f[t-j+1][i]-f[t-j][i]+mod))%mod;
				sum[i][j][1]=(sum[i-1][j][1]+1ll*imul[j-1][i]*(f[t-j+1][i]-f[t-j][i]+mod))%mod;
			}
		}
	}
	for(int i=1;i<=n;i++){
		g[i]=f[i][n];
		for(int j=1;j<i;j++)
			g[i]=(g[i]-1ll*C(i,j)*g[j]%mod+mod)%mod;
		ans=(ans+1ll*C(m,i)*g[i])%mod;
	}
	printf("%d\n",ans);
	return 0;
}

023 CFgym102979E Expected Distance

两点距离还是根据期望线性性拆成到根距离之和减去 lca 期望到根距离。

\(f_i\)\(i\) 期望到根距离,转移显然。

我们考虑怎么求两个点 \(x,y(x<y)\) 的 lca 期望到根距离 \(h(x,y)\),考察 \(y\) 祖先中最深的编号大于 \(x\) 的结点 \(z\),可以发现 \(z\) 取值的概率分布和权值 \(a\) 相同(即与 \(y\) 无关),列出转移:

\[h(x,y)=\frac{a_xf_x+\sum_{i=1}^{x-1}h(i,x)}{\sum_{i=1}^x a_i} \]

可以发现式子与 \(y\) 无关,令 \(g_i=h(i,*)\) 即可。

复杂度 \(O(n)\),如果懒可以带个 \(\log\)

#include<stdio.h>
const int maxn=300005,mod=1000000007;
int n,m;
int a[maxn],c[maxn],f[maxn],g[maxn],v[maxn];
int ksm(int a,int b){
	int res=1;
	while(b){
		if(b&1)
			res=1ll*res*a%mod;
		a=1ll*a*a%mod,b>>=1;
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1,s=0;i<n;i++)
		scanf("%d",&a[i]),s+=a[i],v[i]=ksm(s,mod-2);
	for(int i=1;i<=n;i++)
		scanf("%d",&c[i]);
	for(int i=2,sf=1ll*a[1]*c[1]%mod,sg=0;i<=n;i++){
		f[i]=(c[i]+1ll*sf*v[i-1])%mod,g[i]=1ll*(1ll*a[i]*f[i]+sg)%mod*v[i]%mod;
		sf=(sf+1ll*a[i]*(f[i]+c[i]))%mod,sg=(sg+1ll*a[i]*g[i])%mod;
	}
	for(int i=1,x,y;i<=m;i++)
		scanf("%d%d",&x,&y),printf("%d\n",x==y? 0:(f[x]+f[y]-2ll*g[x<y? x:y]+mod+mod)%mod);
	return 0;
}
posted @ 2022-07-03 20:11  xiaoziyao  阅读(158)  评论(0)    收藏  举报