zoukankan      html  css  js  c++  java
  • 6553. 【GDOI2020模拟4.11】人生

    题目大意

    数轴上排列着(n)个点,点的颜色有黑白两种,部分点已经确定颜色,部分点没有确定。
    每个点可以任意向右边的点连边,可以连可以不连。
    求交错路径(相邻的两个点颜色互异)总数为奇数的图的方案数。
    (nleq 200000)


    思考历程

    早上在打SCOI2018,所以没有做比赛。
    下午的时候思考了一下,想到了个维护异或卷积前缀和来辅助转移的方法。
    后来发现跟正解完全对不上,原来是这个想法本来就考虑不周到。


    正解

    辣鸡DP。
    (end(i))表示以(i)结尾的路径的条数。
    首先可以搞出最简单的状态:(f_{i,j,k,0/1}),表示前(i)个点,有(j)个点为白色并且(end)为奇数,有(k)个点为黑色并且(end)为偶数,路径总数(即(sum_{x=1}^{i} end(x)))是偶数还是奇数,这个状态下的方案数。
    转移可以做到(O(1)):
    (f_{i,j,k,t} imes c(k,0) imes 2^{i-k} o f_{i+1,j+1,k,t^1})
    (f_{i,j,k,t} imes c(k,1) imes 2^{i-k} o f_{i+1,j,k,t})
    (f_{i,j,k,t} imes c(j,0) imes 2^{i-j} o f_{i+1,j,k+1,t^1})
    (f_{i,j,k,t} imes c(j,1) imes 2^{i-j} o f_{i+1,j,k,t})
    其中(c(n,0/1))表示(n)个点中选择偶数或奇数个点的方案数。
    然后就可以(O(n^3))

    接下来可以优化一下这条东西。
    (c(k,0)=C_{k}^{0}+C_{k}^{2}+C_{k}^{4}+...=C_{k-1}^0+C_{k-1}^1+C_{k-1}^2+...)
    (k=0)时,(c(k,0)=1)
    (k>0)时,(c(k,0)=2^{k-1})
    同理,(c(k,1)=C_{k}^{1}+C_{k}^3+C_{k}^5=C_{k-1}^0+C_{k-1}^1+C_{k-1}^2+...)
    (k=0)时,(c(k,1)=0)
    (k>0)时,(c(k,1)=2^{k-1})
    于是可以优化DP状态:(f_{i,0/1,0/1,0/1}),时间复杂度(O(n))


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 200010
    #define ll long long
    #define mo 998244353
    int n;
    ll pow2[N];
    int a[N];
    int f[N][2][2][2];
    inline void add(int &a,ll b){a=(a+b)%mo;}
    int main(){
    	freopen("life.in","r",stdin);
    	freopen("life.out","w",stdout);
    //	freopen("in.txt","r",stdin);
    	scanf("%d",&n);
    	pow2[0]=1;
    	for (int i=1;i<=n;++i)
    		pow2[i]=pow2[i-1]*2%mo;
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]);
    	if (a[1]!=1) f[1][1][0][1]=1;
    	if (a[1]!=0) f[1][0][1][1]=1;
    	for (int i=1;i<n;++i)
    		for (int j=0;j<2;++j)
    			for (int k=0;k<2;++k)
    				for (int t=0;t<2;++t){
    					ll tmp=f[i][j][k][t];
    					if (tmp==0)
    						continue;
    					if (a[i+1]!=1){
    						add(f[i+1][1][k][t^1],tmp*(k?pow2[i-1]:pow2[i]));
    						add(f[i+1][j][k][t],tmp*(k?pow2[i-1]:0));
    					}
    					if (a[i+1]!=0){
    						add(f[i+1][j][1][t^1],tmp*(j?pow2[i-1]:pow2[i]));
    						add(f[i+1][j][k][t],tmp*(j?pow2[i-1]:0));
    					}
    				}
    	ll ans=f[n][0][0][1]+f[n][0][1][1]+f[n][1][0][1]+f[n][1][1][1];
    	printf("%lld
    ",ans%mo);
    	return 0;
    }
    

    总结

    DP问题的常见套路:先搞出个效率低下的DP,然后优化优化再优化。

  • 相关阅读:
    vi使用方法详细介绍
    Jenkins实现Android自动化打包
    JSON知识总结
    React Native中pointerEvent属性
    从零学React Native之06flexbox布局
    Android Http实现文件的上传和下载
    从零学React Native之05混合开发
    React Native声明属性和属性确认
    从零学React Native之04自定义对话框
    Android 在图片的指定位置添加标记
  • 原文地址:https://www.cnblogs.com/jz-597/p/12682138.html
Copyright © 2011-2022 走看看