热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

【ARC068F】Solitaire(dp,计数,思维)

首先发现那个双端队列一定长这样:也就是说,这个队列中的数先单调递减,然后再单调递增,最小值为\(1\)。现在考虑从双端队列中取数,那么当我们取到\(1\)这个数时,我们会在原来的双

首先发现那个双端队列一定长这样:

也就是说,这个队列中的数先单调递减,然后再单调递增,最小值为 \(1\)

现在考虑从双端队列中取数,那么当我们取到 \(1\) 这个数时,我们会在原来的双端队列中取到类似这样的两个数列:(分别用红、蓝表示)

那么红、蓝两数列的总长度为 \(k\),剩下的就是绿色的一段,长度为 \(n-k\),我们每次可以从两端任意取,共取 \(n-k-1\) 次,方案数为 \(2^{n-k-1}\)

所以我们只用考虑前 \(k\) 个数的取法,然后乘上 \(2^{n-k-1}\) 就好了。

由图像,我们看一下弹出序列需要满足什么条件:



  1. 首先第 \(k\) 位肯定是 \(1\)。(题目要求)



  2. \(k\) 个数,一定是由一个或两个单调递减的数列混合而成的(这两个数列就是图中的红、蓝两个数列),并且其中的一个单调递减的数列肯定包含 \(1\) 这个点(蓝色数列)。



  3. \(n-k\) 个数,其最大值应小于某一个提到的单调数列的最小值。也就是绿色段的最大值小于红色段的最小值。



那我们不妨设 \(f(i,j)\) 表示已经取了前 \(i\) 个数,他们的最小值为 \(j\) 时的方案数。(满足上面的 \(3\) 个条件)

那么我们设选的这 \(i\) 个数分成的两个单调递减的序列分别为 \(A\)\(B\),其中最小值 \(j\)\(A\) 中,\(B\) 中的最小值为 \(minn\)

设除了我们选的这 \(i\) 个数剩下的 \(n-i\) 个数组成的集合为 \(S\)\(S\) 中最大的数为 \(maxS\)

大概可以用这张图表示:

\(f(i,j)\) 的定义得,\(B\) 序列已经满足了第 \(3\) 个条件,即 \(maxS

我们现在要从 \(S\) 中选一个数,加入 \(A\)\(B\) 里面。

考虑构造:



  1. 假设加入的数 \(x\) 满足 \(x,那么我们就把它加到 \(A\) 的末尾,此时仍满足所有条件。所以 \(f(i,j)\)\(f(i+1,1)\sim f(i+1,j-1)\) 转移。



  2. 假设加入的数 \(x\) 满足 \(x\geq j\),如果加入 \(A\) 中,\(A\) 就不满足单调递减的性质了,所以只能加入 \(B\) 中。

    然后发现只能把 \(S\) 中的最大值 \(maxS\) 加入到 \(B\) 的队尾(而且必须得满足 \(maxS\geq j\),不然会在第一种情况中算重)。

    由于 \(maxS\)\(S\) 中的最大的数,所以当 \(maxS\) 加入 \(B\) 数列成为 \(B\) 数列的最小值后,仍大于 \(S\) 中的所有数,满足条件。

    所以 \(f(i,j)\)\(f(i+1,j)\) 转移。

    至于为什么 \(S\) 中除 \(maxS\) 的某一个数 \(x\) 都不能加入 \(B\) 的队尾:因为加入后,\(B\) 中的最小值就变成了 \(x\),而 \(S\) 中的最大值仍为 \(maxS\)。此时 \(x,不满足条件。



所以通过 \(f(i,j)\) 就可以向 \(f(i+1,1\sim j)\) 转移了。

显然答案就是 \(2^{n-k-1}\times\sum\limits_{i=2}^{n-k+2}f(k-1,i)\)

还是有很多细节的,详见代码:

#include
#define N 2010
#define ll long long
#define mod 1000000007
using namespace std;
int n,k;
ll f[N][N];
ll poww(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b>>=1;
}
return ans;
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=2;i<=n;i++) f[1][i]=1;
for(int i=1;i {
f[i+1][n-i+1]=f[i][n-i+1];
for(int j=n-i;j>=2;j--)
f[i+1][j]=(f[i+1][j+1]+f[i][j])%mod;
}
ll ans=0;
for(int i=2;i<=n-k+2;i++)
ans=(ans+f[k-1][i])%mod;
if(k==1) ans=1;//特判1:k=1
if(n-k-1>=0) printf("%lld\n",ans*poww(2,n-k-1)%mod);
else printf("%lld\n",ans);//特判2:n=k
return 0;
}


推荐阅读
  • 深入探讨:Actor模型如何解决并发与分布式计算难题
    在现代软件开发中,高并发和分布式系统的设计面临着诸多挑战。本文基于Akka最新文档,详细探讨了Actor模型如何有效地解决这些挑战,并提供了对并发和分布式计算的新视角。 ... [详细]
  • 本文详细介绍了二叉堆的概念及其在Java中的实现方法。二叉堆是一种特殊的完全二叉树,具有堆性质,常用于实现优先队列。 ... [详细]
  • 深入解析Python进程间通信:Queue与Pipe的应用
    本文详细探讨了Python中进程间通信的两种常用方法——Queue和Pipe,并通过具体示例介绍了它们的基本概念、使用方法及注意事项。 ... [详细]
  • top 命令是一个强大的工具,可以实时动态地监控系统的整体运行状况。它整合了多种信息,提供了一个全面的系统性能和运行信息视图。通过 top 命令的交互界面,用户可以使用热键进行各种管理操作。 ... [详细]
  • 前言:由于Android系统本身决定了其自身的单线程模型结构。在日常的开发过程中,我们又不能把所有的工作都交给主线程去处理(会造成UI卡顿现象)。因此,适当的创建子线程去处理一些耗 ... [详细]
  • Java中的引用类型详解
    本文详细介绍了Java中的引用类型,包括强引用、软引用、弱引用和虚引用的特点和应用场景。 ... [详细]
  • 本文详细介绍了Sleep函数的基本概念、使用方法及其背后的实现原理。适合对Sleep函数的使用和实现感兴趣的开发者阅读。通过本文,您将了解如何在不同操作系统中使用Sleep函数,以及其在多线程编程中的重要性。 ... [详细]
  • SDWebImage第三方库学习
    1、基本使用方法异步下载并缓存-(void)sd_setImageWithURL:(nullableNSURL*)urlNS_REFINED_FOR_SWIFT;使用占位图片& ... [详细]
  • 关于进程的复习:#管道#数据的共享Managerdictlist#进程池#cpu个数1#retmap(func,iterable)#异步自带close和join#所有 ... [详细]
  • 大华股份2013届校园招聘软件算法类试题D卷
    一、填空题(共17题,每题3分,总共51分)1.设有inta5,*b,**c,执行语句c&b,b&a后,**c的值为________答:5 ... [详细]
  • 本文详细介绍了如何对一个整数的二进制表示进行逆序操作。通过多种方法,包括直接法、查表法和分治法,帮助读者全面理解和掌握这一技术。 ... [详细]
  • Spring Boot + RabbitMQ 消息确认机制详解
    本文详细介绍如何在 Spring Boot 项目中使用 RabbitMQ 的消息确认机制,包括消息发送确认和消息接收确认,帮助开发者解决在实际操作中可能遇到的问题。 ... [详细]
  • Redis 是一个高性能的开源键值存储系统,支持多种数据结构。本文将详细介绍 Redis 中的六种底层数据结构及其在对象系统中的应用,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。通过12张图解,帮助读者全面理解 Redis 的数据结构和对象系统。 ... [详细]
  • 我自己做了一个网站图片的抓取,感觉速度有点慢抓取4000张图片可能得用15分钟左右的时间,我百度看用线程可以加快抓取,然后创建了5个线程抓取,但是5个线程是同步执行同样的操作一个图片就 ... [详细]
  • 在iOS开发中,多线程技术的应用非常广泛,能够高效地执行多个调度任务。本文将重点介绍GCD(Grand Central Dispatch)在多线程开发中的应用,包括其函数和队列的实现细节。 ... [详细]
author-avatar
趣校区导购网
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有