[DP记录]AT5142 [AGC035E] Develop

command_block

2021-04-30 10:36:56

Personal

**题意** : 初始时,在黑板上写有 $-10^{18}$ 到 $10^{18}$ 中的所有整数。 每次你可以选中一个 $[1,n]$ 中还在黑板上的整数 $x$ ,把它擦去并补写上 $x-2$ 与 $x+k$ (如果原来不存在的话)。 你可以进行这个操作任意次(可以不进行),求最终黑板上数字的可能状态有多少种,答案对给定整数 $m$ 取模。 $k\leq n\leq 150$ ,时限$\texttt{5s}$。 ------------ 考虑如何判定一种状态能否达到。 若最终 $x$ 不在黑板上,则 $x$ 一定要后于 $x+2$ 与 $x-k$ 被擦去,因为两者被擦去时会写回 $x$。 于是,连边 $x+2\rightarrow x$ 和 $x-k\rightarrow x$ ,这个有向图的每一个拓扑序都是一种合法的擦除方案,若有向图中有环,则无解。 现在问题能转化为 : 问 $[1,n]$ 的多少个子集形成的有向图是无环的。 先把 $[1,n]$ 的完整图画出来,观察会产生怎样的环 : ![](https://cdn.luogu.com.cn/upload/image_hosting/fdpffocd.png?x-oss-process=image/resize,m_lfit,w_360) (from @关怀他人)(这张图是 $k$ 为奇数的情况) 将奇数和偶数分别画成两排。(记偶数集合为 $Z_0$ ,奇数集合为 $Z_1$) - 若 $k$ 为偶数,则在一排中选择达到 $k/2+1$ 个连续的数,就会形成环。 两条链互不干涉,方案数是乘起来的。对于每条链分别简单 $\rm DP$ 即可。 - 若 $k$ 为奇数,则如上图。将 $x\rightarrow x+k$ 的边画在两条链中间。 不难发现,只需要考虑那些含有两条横边的环。 所有的环都形如 : 从左侧某个点开始,向上走一段,向右走一次,向上走一段,回到原点。 如图中 $5\rightarrow 3\rightarrow 6\rightarrow 4\rightarrow 2\rightarrow 5$。 考虑 $\rm DP$ 。设 $dp[i][j][k]$ 表示考虑了前 $i$ 层(如图中横着的边),最长上右上路径长度达到 $j$ ,右侧偶数链连续选数 $k$ 个的方案数。 若 $j$ 达到 $k+2$ ,则会产生一个环。 选中左侧奇数,可以延续右上右路径(前提是得有) 当左侧奇数不选时,会切断上右上路径。 当左侧奇数和右侧偶数同时选中时,会产生新的右上右路径,长度为 $k+1$。 具体转移见代码。 复杂度 $O(n^2k)$。 ```cpp #include<algorithm> #include<cstdio> using namespace std; int n,t,mod,f[82][155][82],g[82][82],pw2[82]; int main() { scanf("%d%d%d",&n,&t,&mod); if (t&1){ pw2[0]=1; for (int i=1;i<(t/2);i++)pw2[i]=(pw2[i-1]<<1)%mod; for (int i=0;i<=(t/2);i++) f[0][0][i]=pw2[max((t/2)-i-1,0)]; for (int i=1;2*i-1<=n;i++){ if (2*i-1+t<=n){ for (int j=0;j<=t+1;j++) for (int k=0;k<=n/2;k++) if (f[i-1][j][k]){ int sav=f[i-1][j][k]; if (j>0){ f[i][0][0]=(f[i][0][0]+sav)%mod; f[i][0][k+1]=(f[i][0][k+1]+sav)%mod; f[i][j+1][0]=(f[i][j+1][0]+sav)%mod; f[i][j+1][k+1]=(f[i][j+1][k+1]+sav)%mod; }else { f[i][0][0]=(f[i][0][0]+2ll*sav)%mod; f[i][0][k+1]=(f[i][0][k+1]+sav)%mod; f[i][k+2][k+1]=(f[i][k+2][k+1]+sav)%mod; } } }else { for (int j=0;j<=t+1;j++) for (int k=0;k<=n/2;k++) if (f[i-1][j][k]){ int sav=f[i-1][j][k]; if (j>0){ f[i][0][0]=(f[i][0][0]+sav)%mod; f[i][j+1][0]=(f[i][j+1][0]+sav)%mod; }else f[i][0][0]=(f[i][0][0]+2ll*sav)%mod; } } } int ans=0; for (int j=0;j<=t+1;j++) for (int k=0;k<=n/2;k++) ans=(ans+f[(n+1)/2][j][k])%mod; printf("%d",ans); }else { g[0][0]=1;t/=2; for (int i=0;i<(n+1)/2;i++) for (int j=0;j<=min(i,t);j++){ if (j+1<=t) g[i+1][j+1]=(g[i+1][j+1]+g[i][j])%mod; g[i+1][0]=(g[i+1][0]+g[i][j])%mod; } int ans1=0,ans2=0; for (int i=0;i<=t;i++){ ans1=(ans1+g[n/2][i])%mod; ans2=(ans2+g[(n+1)/2][i])%mod; }printf("%lld",1ll*ans1*ans2%mod); }return 0; } ```