[国家集训队] calc
嘟嘟嘟
这道题dp虽然不难,但是我还是没推出来,感觉最近脑子不太好用啊。
于是就跑去问神仙gjx(全国前三!)了。(外出集训真是好)
神仙不愧是神仙,一会儿就想出来了,而且方法还比网上的题解好懂。
dp[i][j]表示用值域为[1, i]的数,凑出的所有合法序列的值的和。
然后规定序列必须是严格递增的,这样答案再乘以一个\(n!\)就行了。
转移的时候,我们考虑\(i\)这个数用不用上,于是有:\(dp[i][j] = dp[i - 1][j] +i * dp[i - 1][j - 1]\)。现在感觉这个挺好理解的,如果用上\(i\)了,\(i\)就必须放在最后面,并且答案要乘以\(i\)。
但这个复杂度\(O(nA)\)的,\(A\)太大不好办。
然后gjx大佬告诉我,这个dp式是一个关于\(j\)的\(2n\)次多项式,然后给我证了一下,但还是不太懂:
首先知道\(dp[i][1] = \frac{i * (i + 1)}{2}\),猜想这可能是一个\(2n\)次的。
然后把转移方程中的\(dp[i - 1][j]\)不断展开,得到了\(dp[i][j] = \sum _ {k = 0} ^ {i - 1} dp[k][j] *(k + 1)\)。于是发现,这是一个关于\(j\)的\(2n\)次多项式。
(感觉自己还是不太理解)
(这篇题解的证明好像也不错, 应该是数学归纳法,我得学一下)
知道这是个多项式,就可以拉格朗日差值了。
注意dp的边界取值,我这里debug了好久。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 505;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int A, n, mod;
ll fac[maxn << 1], dp[maxn << 1][maxn];
In ll quickpow(ll a, ll b)
{
ll ret = 1;
for(; b; b >>= 1, a = a * a % mod)
if(b & 1) ret = ret * a % mod;
return ret;
}
ll inv[maxn << 1], pre[maxn << 1], suf[maxn << 1];
In ll calc(int n)
{
inv[n] = quickpow(fac[n], mod - 2);
for(int i = n - 1; i >= 0; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
pre[0] = A;
for(int i = 1; i <= n; ++i) pre[i] = pre[i - 1] * (A - i) % mod;
suf[n + 1] = 1;
for(int i = n; i >= 0; --i) suf[i] = suf[i + 1] * (A - i) % mod;
ll ret = 0;
for(int i = 0; i <= n; ++i)
{
ll tp = (i - 1 < 0 ? 1 : pre[i - 1]) * suf[i + 1] % mod * inv[i] % mod * inv[n - i] % mod;
if((n - i) & 1) tp = -tp;
ret = (ret + dp[i][n >> 1] * tp % mod + mod) % mod;
}
return ret;
}
int main()
{
A = read(), n = read(), mod = read();
fac[1] = 1;
for(int i = 2; i <= (n << 1); ++i) fac[i] = fac[i - 1] * i % mod;
dp[0][0] = 1;
for(int i = 1; i <= (n << 1); ++i)
{
dp[i][1] = i * (i + 1) / 2;
dp[i][i] = fac[i];
for(int j = 2; j < i; ++j)
dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] * i % mod) % mod;
}
if(A <= (n << 1)) write(dp[A][n] * fac[n] % mod), enter;
else write(calc(n << 1) * fac[n] % mod), enter;
return 0;
}
[国家集训队] calc的更多相关文章
- [国家集训队] calc(动规+拉格朗日插值法)
题目 P4463 [国家集训队] calc 集训队的题目真是做不动呀\(\%>\_<\%\) 朴素方程 设\(f_{i,j}\)为前\(i\)个数值域\([1,j]\),且序列递增的总贡献 ...
- P4463 [国家集训队] calc(拉格朗日插值)
传送门 设\(dp[i][j]\)为考虑\(i\)个数,其中最大值不超过\(j\)的答案,那么转移为\[dp[i][j]=dp[i-1][j-1]\times i\times j+dp[i][j-1] ...
- Luogu P4463 [国家集训队] calc
WJMZBMR的题果然放在几年后看来仍然挺神,提出了一种独特的优化DP的方式 首先我们想一个暴力DP,先定下所有数的顺序(比如强制它递增),然后最后乘上\(n!\)种排列方式就是答案了 那么我们容易想 ...
- p4463 [国家集训队] calc
分析 代码 #include<bits/stdc++.h> using namespace std; ][],Ans; inline int pw(int x,int p){ ; whil ...
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)
2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 7676 Solved: 3509[Subm ...
- bzoj2152 / P2634 [国家集训队]聪聪可可(点分治)
P2634 [国家集训队]聪聪可可 淀粉质点分治板子 边权直接 mod 3 直接点分治统计出所有的符合条件的点对再和总方案数约分 至于约分.....gcd搞搞就好辣 #include<iostr ...
- [洛谷P1527] [国家集训队]矩阵乘法
洛谷题目链接:[国家集训队]矩阵乘法 题目背景 原 <补丁VS错误>请前往P2761 题目描述 给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 输入输出格式 输入 ...
- P4827「国家集训队」 Crash 的文明世界
「国家集训队」 Crash 的文明世界 提供一种不需要脑子的方法. 其实是看洛谷讨论版看出来的( (但是全网也就这一篇这个方法的题解了) 首先这是一个关于树上路径的问题,我们可以无脑上点分治. 考虑当 ...
- BZOJ 2039: [2009国家集训队]employ人员雇佣
2039: [2009国家集训队]employ人员雇佣 Time Limit: 20 Sec Memory Limit: 259 MBSubmit: 1369 Solved: 667[Submit ...
随机推荐
- SignalR的简单实现消息广播
之前由于一个项目的需要(简单说一下,一个网页游戏,裁判的页面点击开始按钮,玩家便可以开始游戏),研究了很久,最终一个同事跟我推荐了SignalR.距离项目结束已经有一段时间了,再来回顾一下Signal ...
- DataGridView 多列排序功能
System.Data.DataTable dt = new System.Data.DataTable(); private void FillDataGridView() { dt.Columns ...
- 用CSS控制DIV居中失效的解决方法
1.一般情况下DIV居中失效是因为没写DTD语句 在页面的最上方加上: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitiona ...
- 【实践练习一】Git以及Github的使用
以前经常在同学大神那听说过Github这神器,虽敬佩久已,奈何却无缘使用.好吧,我承认,主要还是不会用,一看网站全是英文的,想想还是不要为难自己了.然而现在还是要为难自己了,趁着早上刚学 ...
- TCP握手过程中建连接的流程和队列
这里有两个队列:syns queue(半连接队列):accept queue(全连接队列). 三次握手过程中: 第一步: server 收到 client 的 syn 后,把这个连接信息放到半连接队列 ...
- c3p0死锁
1.APPARENT DEADLOCK!!! Creating emergency threads for unassigned pending tasks! 抛出以下异常信息: com.mchang ...
- 移动端安卓手机不能识别border 0.5px解决方案
由于安卓手机无法识别border 0.5px,因此我们要用0.5px的话必须要借助css3中的-webkit-transform:scale缩放来实现, 原理:将伪元素的宽设为200%,height设 ...
- JavaScript长时间未操作自动退出登录
主要是通过mouseover 来监听有没有进行当前页面操作,通过未操作时间和设定退出的时间做比较,从而退出登录. var oldTime = new Date().getTime(); var new ...
- Ubuntu18---安装Redis和简单使用Redis
前言 Redis是常用基于内存的Key-Value数据库,比Memcache更先进,支持多种数据结构,高效,快速.用Redis可以很轻松解决高并发的数据访问问题:作为实时监控信号处理也非常不错. 环境 ...
- scrapy系列(三)——基础spider源码解析
前面两章介绍了scrapy的安装和项目的新建,那么这一章就讲讲spider吧. scrapy有个命令是runspider, 这个命令的作用就是将一个spider当做一个python文件去执行,而不用创 ...