CSP-S2020 DP专项训练
前言
\(\text{CPS-S2020}\) 已然临近,而 \(\text{DP}\) 作为联赛中的常考内容,是必不可少的复习要点,现根据教练和个人刷题,整理部分好题如下(其实基本上是直接搬……)。
CF515C Drazil and Factorial
题目大意
定义 \(F(x)=\sum_{i=0}^{10^i<=x} ((x/10^i) \mod 10) !\) 。
给定一个十进制数 \(a\),共由 \(n\) 个数字组成。要找到最大正数 \(x\) ,满足以下两个条件:
- \(x\) 不包含任何数字 \(0\) 和数字 \(1\) 。
- \(F(x)=F(a)\) 。
题解
感觉很高级,然后发现是个打表题:
#include<bits/stdc++.h>
using namespace std;
const int N=25;
int n,a[N];
vector<int> bag[N];
vector<int> res;
int main()
{
bag[2].push_back(2);
bag[3].push_back(3);
bag[4].push_back(2);
bag[4].push_back(2);
bag[4].push_back(3);
bag[5].push_back(5);
bag[6].push_back(3);
bag[6].push_back(5);
bag[7].push_back(7);
bag[8].push_back(2);
bag[8].push_back(2);
bag[8].push_back(2);
bag[8].push_back(7);
bag[9].push_back(2);
bag[9].push_back(3);
bag[9].push_back(3);
bag[9].push_back(7);
cin>>n;
for(int i=1;i<=n;++i)
{
scanf("%1d",&a[i]);
for(int j=0;j<(int)bag[a[i]].size();++j)
res.push_back(bag[a[i]][j]);
}
sort(res.begin(),res.end());
for(int i=(int)res.size()-1;i>=0;--i) printf("%d",res[i]);
printf("\n");
return 0;
}
然后瞎搞一下就做完了。
CF840C On the Bench
题目大意
给定一个序列 \(a(a_i\le 10^9)\) ,长度为 \(n(n\le 300)\) 。
试求有多少 \(1\) 到 \(n\) 的排列 \(p_i\) ,满足对于任意的 \(2\le i\le n\) 有 \(a_{p_{i-1}}\times a_{p_i}\) 不为完全平方数,答案对 \(10^9+7\) 取模。
题解
要先进行一次很巧妙的简化,不难想到,如果我们把每一个数内部的平方因子都除去,最后是不会对答案造成影响的,此时如果要形成完全平方数,只能是两个相等的数相乘。于是我们就将问题转化为了有多少种排列,相邻的数不能相等。这是一个板子题(板子我都不会)。
然后有两种思考方向,排列组合或者是老老实实 \(\text{DP}\) ,我们选择后者(前者暂时不会,会补上的)。
定义 \(f_{i,j,k}\) 表示对于第 \(i\) 个数,前面有 \(j\) 组相邻的数相同, \(j\) 组中又有 \(k\) 组等于 \(a_i\) 。那么考虑将 \(a_i\) 放入,有 \(3\) 种情况,我们不妨设在 \(i\) 之前,已经有 \(same\) 个与 \(a_i\) 相等的数放入其中了:
- 产生新的相邻的数,位置数为 \(2same-k\) 。
- 断开相邻的数,位置数为 \(j-k\) 。
- 什么都没发生,位置数为 \(i-(2same-k)-(j-k)\) 。
用上述方法转移即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=305;
const int MOD=1e9+7;
int n,a[N];
int f[N][N][N];
signed main()
{
cin>>n;
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=n;++i)
{
for(int j=2;j*j<=a[i];++j)
while(a[i]%(j*j)==0) a[i]/=j*j;
}
sort(a+1,a+1+n);
int tmp=1;f[1][0][0]=1;
for(int i=2;i<=n;++i)
{
if(a[i]!=a[i-1])
{
for(int j=0;j<i-1;++j)
{
for(int k=1;k<=min(j,tmp-1);++k)
f[i-1][j][0]+=f[i-1][j][k],f[i-1][j][k]=0,f[i-1][j][0]%=MOD;
}
tmp=0;
}
// printf("%d %d\n",i,tmp);
for(int j=0;j<i;++j)
{
for(int k=0;k<=min(j,tmp);++k)
{
if(j&&k) f[i][j][k]+=f[i-1][j-1][k-1]*(tmp*2-k+1)%MOD,f[i][j][k]%=MOD;
f[i][j][k]+=f[i-1][j+1][k]*(j+1-k)%MOD,f[i][j][k]%=MOD;
f[i][j][k]+=f[i-1][j][k]*(i-(j-k)-(tmp*2-k))%MOD,f[i][j][k]%=MOD;
// printf("%lld %lld %lld %lld\n",i,j,k,f[i][j][k]);
}
}
tmp++;
}
printf("%lld\n",f[n][0][0]);
return 0;
}
P4766 [CERC2014]Outer space invaders
题目大意
你要消灭所有的外星人。对于每一个外星人,你必须在一段区间时间 $\left [ l_i,r_i \right ] $ 内将其消灭,同时他距离你 \(d_i\) 。
你有一种武器,你可以在任意时间使用他,消耗 \(r_i\) 的能量消灭此时距离你为 \(r_i\) 以内的外星人,问消灭所有的外星人的最少代价。
题解
这里他很巧妙的转化了问题。不难想到,如果是要消灭所有的外星人,必定是需要消除最大的一个的。如果我们选择在一个时间 \(t_i\) 消灭了当前最远的外星人,那么整一个时间轴会被分成 \(3\) 部分,时间轴完全位于 \(t_i\) 左右两边的外星人,和跨过 \(t_i\) 的外星人,而又因为我们消灭的外星人是当前区间最大的,所以跨过的这一部分外星人就会被消灭。
这样左右两部分又变成了和一开始条件相当的区间,可以分治,直到当前区间内没有外星人了。
写法有递归递推两种,都可以的。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=305,MAXN=1e4+5;
int t,n;
struct Alien{int op,ed,dis;}a[N];
int uni[N<<1],mp[MAXN],len=0;
int f[N<<1][N<<1];
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d%d%d",&a[i].op,&a[i].ed,&a[i].dis);
len=0,memset(mp,0,sizeof(mp));
for(int i=1;i<=n;++i) uni[++len]=a[i].op,uni[++len]=a[i].ed;
sort(uni+1,uni+1+len),len=unique(uni+1,uni+1+len)-uni-1;
for(int i=1;i<=len;++i) mp[uni[i]]=i;
for(int i=1;i<=n;++i) a[i].op=mp[a[i].op],a[i].ed=mp[a[i].ed];
memset(f,0,sizeof(f));
for(int i=1;i<=len;++i)
{
for(int l=1,r=i;r<=len;++l,++r)
{
int now=-1;
for(int j=1;j<=n;++j)
if(l<=a[j].op&&a[j].ed<=r&&(now==-1||a[now].dis<a[j].dis)) now=j;
if(now==-1) continue;
f[l][r]=1e9+7;
for(int k=a[now].op;k<=a[now].ed;++k) f[l][r]=min(f[l][r],f[l][k-1]+f[k+1][r]+a[now].dis);
}
}
printf("%d\n",f[1][len]);
return ;
}
int main()
{
cin>>t;
while(t--) solve();
return 0;
}
CF264C Choosing Balls
题目大意
你可以选若干个物品,若这次选择的物品与上次选的 \(c_i\) 相同那么这个的贡献就是 \(a\times v_i\) 否则是 \(b\times v_i\) 。要使得利益最大化。
题解
我们不难考虑到用 \(f_i\) 表示以颜色 \(i\) 为结尾的,到当前的最大利益。然后转移方程就很好写了。
\]
然后又不难想到 \(f_i\) 是可以用线段树维护的,但是发现还是过不了。
仔细思考一下,发现 \(f_i\) 是单调递增的,所以我们只需要维护所有 \(f_i\) 的最大值和次大值就可以了。算是个套路。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5;
inline void cmax(int &a,int b){if(a<b)a=b;}
inline void cmin(int &a,int b){if(a>b)a=b;}
int n,m;
struct Ball{int col,val;}a[N];
int f[N];
int x,y,maxn1,maxn2;
signed main()
{
cin>>n>>m;
// printf("\n--------------\n");
for(int i=1;i<=n;++i) scanf("%lld",&a[i].val);
for(int i=1;i<=n;++i) scanf("%lld",&a[i].col);
while(m--)
{
scanf("%lld%lld",&x,&y);
for(int i=1;i<=n;++i) f[i]=-1e18-7;
maxn1=a[1].col,maxn2=0;
f[a[1].col]=a[1].val*y;
for(int i=2;i<=n;++i)
{
if(maxn1==a[i].col) cmax(f[maxn1],max(f[maxn1]+a[i].val*x,f[maxn2]+a[i].val*y));
else cmax(f[a[i].col],max(f[a[i].col]+a[i].val*x,f[maxn1]+a[i].val*y));
cmax(f[a[i].col],a[i].val*y);
if(a[i].col==maxn1) continue;
if(f[a[i].col]>=f[maxn1]) maxn2=maxn1,maxn1=a[i].col;
else if(f[a[i].col]>f[maxn2]) maxn2=a[i].col;
// printf("%lld %lld\n",f[maxn1],f[maxn2]);
}
printf("%lld\n",max(0ll,f[maxn1]));
}
}
AT2000 [AGC002F] Leftmost Ball
也是一道套路好题。
题目大意
给你 \(n\) 种颜色的球,每个球有 \(k\) 个,把这 \(n\times k\) 个球排成一排,把每一种颜色的最左边出现的球涂成白色(初始球不包含白色),求有多少种不同的颜色序列,答案对 \(10^9+7\) 取模。
题解
因为最后回答的是颜色序列,所以每个相同颜色的小球相同。我们不妨思考最后的合法序列有怎样的特征。
枚举一下可以发现,对于最终序列的任何一个位置,其前缀的白球数量一定不比其前缀的颜色种类(除白色)数少。所以我们不妨设 \(f_{i,j}\) 表示到第 \(i\) 个白球,且已经放完了 \(j\) 种颜色的方案数。
因为我们需要满足不重不漏,所以我们不妨规定每一次放入白球和每一种颜色第一个球的时候,必定放在当前的第一个空位,可以证明这样的操作可以包括所有答案同时不遗漏答案。由此,状态转移方程易得为:
\]
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=2e3+5,K=2e3+5;
const Lint MOD=1e9+7;
int n,k;
Lint fac[N*K];
Lint ksm(Lint x,int k){Lint res=1;for(;k;k>>=1,x=x*x%MOD)if(k&1)res=res*x%MOD;return res;}
Lint cal(int n,int m){return fac[n]*ksm(fac[m],MOD-2)%MOD*ksm(fac[n-m],MOD-2)%MOD;}
Lint f[N][N];
int main()
{
cin>>n>>k;
if(k==1){printf("1\n");return 0;}
fac[0]=1;
for(int i=1;i<=n*k;++i) fac[i]=fac[i-1]*i%MOD;
f[0][0]=1;
for(int i=1;i<=n;++i)
{
for(int j=0;j<=i;++j)
{
f[i][j]=f[i-1][j];
if(j) f[i][j]+=f[i][j-1]*(n-j+1)%MOD*cal(n*k-i-(j-1)*(k-1)-1,k-2)%MOD,f[i][j]%=MOD;
}
}
printf("%lld\n",f[n][n]);
return 0;
}
AT4513 [AGC030D] Inversion Sum
超级有意思的转换,感觉自己根本想不到啊。
题目大意
给你一个长度为 \(n\) 的数列,然后给你 \(q\) 个操作,你可以选择操作或者不操作,问所有情况下逆序对的总和。
题解
一眼感觉毫无思路。然后考虑移动之后的逆序对个数的变化贡献,发现难以维护两者的大小关系,但是发现我们可以计算概率,即 \(i\) 比 \(j\) 大的概率。
因为概率乘上总次数,就是每一个情况的次数,我们对于两个位置 \(i\) 和 \(j\) ,我们只需要知道最后有多少种情况是 \(i\) 比 \(j\) 大的,所以我们不妨设 \(f_{i,j}\) 表示当前(可能已经进行了几次交换) \(a_i\) 比 \(a_j\) 大的概率,然后对于每一种交换,我们只需要找到与其相关的几个 \(f_{i,j}\) 进行更新就可以了,复杂度 \(O(n(n+q))\) ,可以接受。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Lint long long
const int N=3e3+5,M=3e3+5;
const Lint MOD=1e9+7;
int n,m,a[N];
struct Opt{int x,y;}b[M];
Lint ksm(Lint x,int k){Lint res=1;for(;k;k>>=1,x=x*x%MOD)if(k&1)res=res*x%MOD;return res;}
Lint f[N][N];
Lint inv=ksm(2,MOD-2);
Lint All,res=0;
int main()
{
cin>>n>>m,All=ksm(2ll,m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i) scanf("%d%d",&b[i].x,&b[i].y);
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
f[i][j]=(a[i]>a[j]);
}
for(int i=1;i<=m;++i)
{
for(int j=1;j<=n;++j)
{
if(b[i].x==j||b[i].y==j) continue;
Lint tmp1=f[b[i].x][j],tmp2=f[b[i].y][j];
f[b[i].x][j]=(tmp1+tmp2)*inv%MOD;
f[b[i].y][j]=(tmp1+tmp2)*inv%MOD;
}
for(int j=1;j<=n;++j)
{
if(b[i].x==j||b[i].y==j) continue;
Lint tmp1=f[j][b[i].x],tmp2=f[j][b[i].y];
f[j][b[i].x]=(tmp1+tmp2)*inv%MOD;
f[j][b[i].y]=(tmp1+tmp2)*inv%MOD;
}
Lint tmp1=f[b[i].x][b[i].y],tmp2=f[b[i].y][b[i].x];
f[b[i].x][b[i].y]=f[b[i].y][b[i].x]=(tmp1+tmp2)*inv%MOD;
}
for(int i=1;i<=n;++i)
{
for(int j=i+1;j<=n;++j)
res+=f[i][j]*All%MOD,res%=MOD;
}
printf("%lld\n",res);
}
CSP-S2020 DP专项训练的更多相关文章
- DP专题训练之HDU 2955 Robberies
打算专题训练下DP,做一道帖一道吧~~现在的代码风格完全变了~~大概是懒了.所以.将就着看吧~哈哈 Description The aspiring Roy the Robber has seen a ...
- dp专题训练
****************************************************************************************** 动态规划 专题训练 ...
- DP专题训练之HDU 1087 Super Jumping!
Description Nowadays, a kind of chess game called "Super Jumping! Jumping! Jumping!" is ve ...
- DP专题训练之HDU 1231 最大连续子序列
Description 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j < ...
- DP专题训练之HDU 1864 最大报销额
做DP一定要注意数组的大小,嗯,就是这样~ Description 现有一笔经费可以报销一定额度的发票.允许报销的发票类型包括买图书(A类).文具(B类).差旅(C类),要求每张发票的总额不得超过10 ...
- [SinGuLaRiTy] 树形DP专项测试
[SinGuLaRiTy-1015] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 对于所有的题目:Time Limit:1s | Me ...
- dp周训练 状态压缩
题目链接:题意:给你一个10*10的矩阵,每到一个格子中都要拿一个0-9的数值,求从矩阵左上方走到右下方且必须0-9都经过,拿的数值和最小是多少: #include <iostream> ...
- 1月18日 LCA专项训练
A. Lorenzo Von Matterhorn B.Minimum spanning tree for each edge C.Misha, Grisha and Underground D.Fo ...
- Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664
luogu题目传送门! 首先,硬求可行方案数并不现实,因为不好求(去年考场就这么挂的,虽然那时候比现在更蒟). 在硬搞可行方案数不行之后,对题目要求的目标进行转换: 可行方案数 = 总方案数 - 不合 ...
随机推荐
- 菜鸟试做GUI简单数据库查询界面 python+tkinter+mysql
一.准备工作: 1.安装mysql3.7,创建一个test数据库,创建student表,创建列:(列名看代码),创建几条数据 (以上工作直接用navicat for mysql工具完成) 二.代码: ...
- MyBatis 使用手册
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射.MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作.MyBatis 可以通过简单的 XM ...
- php 序列化和反序列化的作用及使用
1.序列化是什么意思呢? 序列化就是把本来不能直接存储的数据转换成可存储的数据,并且不会丢掉数据格式 serialize(); 2.反序列化是什么意思呢? 其实就是字面的意思,把序列化的数据,转换成我 ...
- python-Requests模块的使用
1. Requests简介 Requests模块是一个用于网络访问的模块,其实类似的模块有很多,比如urllib,urllib2,httplib,httplib2,他们基本都提供相似的功能,那为什么R ...
- centos8 连接wifi
从官网下载的6G多的iso安装后 #ifconfig -a 如下 enp3s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500l ...
- 详细!Mybatis-plus常用API全套教程,我就不信你看完还不懂!
前言 官网:Mybatis-plus官方文档 简化 MyBatis ! 创建数据库 数据库名为mybatis_plus 创建表 创建user表 DROP TABLE IF EXISTS user; C ...
- FL Studio乐理教程之和弦进行
和弦级数 在一个调内,分别由调内7个音为根音组成的和弦总共有7个,每个和弦依次为1-7级和弦.例如在C大调内,以C为根音建立和弦,就是一级和弦,以D为根音建立和弦,即是二级和弦,以此类推. 图1:1- ...
- 将input 的文本框改为不可编辑状态
<input type="text" id = "textid" name="名称" value="值" size ...
- 考研数学数一公式整理(微积分&线性代数&概率统计)
主要根据李永乐老师的线性代数讲义.全书和汤家凤老师的高数讲义整理的. 用于记背数学需要背的公式和步骤,概念.定义.公式多,方法步骤少(毕竟太庞杂了). 本来是自用,但还是分享一下,希望有补充指正! 链 ...
- dubbo与zk
一.总体流程: 1.服务提供者启动时,会向注册中心写入自己的元数据信息,同时会订阅配置元数据信息: 2.消费者启动时,也会向注册中心写入自己的元数据信息,并订阅服务提供者.路由和配置元数据信息: 3. ...