• 202273 #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;
    }
    
  • 相关阅读:
    Java的 Annotation 新特性
    Java 枚举
    Java 泛型
    Linux kali信息探测以及 Nmap 初体验
    静态导入 ()
    Java foreach循环
    Java 可变参数
    炫酷的CSS3响应式表单
    关于CSS选择器连续性的问题
    简述ECMAScript6新增特性
  • 原文地址:https://www.cnblogs.com/xiaoziyao/p/16440738.html
Copyright © 2020-2023  润新知