前言

\(\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\) ,满足以下两个条件:

  1. \(x\) 不包含任何数字 \(0\) 和数字 \(1\) 。
  2. \(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\) 相等的数放入其中了:

  1. 产生新的相邻的数,位置数为 \(2same-k\) 。
  2. 断开相邻的数,位置数为 \(j-k\) 。
  3. 什么都没发生,位置数为 \(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_{c_i}=max(f_{c_i}+a\times v_i,max_{j\ne c_i}f_j+b\times v_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\) 种颜色的方案数。

因为我们需要满足不重不漏,所以我们不妨规定每一次放入白球和每一种颜色第一个球的时候,必定放在当前的第一个空位,可以证明这样的操作可以包括所有答案同时不遗漏答案。由此,状态转移方程易得为:

\[f_{i,j}=f_{i-1,j}+f_{i,j-1}\times (n-j+1)\times C_{n\times k-i-(j-1)\times (k-1)-1}^{k-2}
\]

代码如下:

#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专项训练的更多相关文章

  1. DP专题训练之HDU 2955 Robberies

    打算专题训练下DP,做一道帖一道吧~~现在的代码风格完全变了~~大概是懒了.所以.将就着看吧~哈哈 Description The aspiring Roy the Robber has seen a ...

  2. dp专题训练

    ****************************************************************************************** 动态规划 专题训练 ...

  3. DP专题训练之HDU 1087 Super Jumping!

    Description Nowadays, a kind of chess game called "Super Jumping! Jumping! Jumping!" is ve ...

  4. DP专题训练之HDU 1231 最大连续子序列

    Description 给定K个整数的序列{ N1, N2, ..., NK },其任意连续子序列可表示为{ Ni, Ni+1, ..., Nj },其中 1 <= i <= j < ...

  5. DP专题训练之HDU 1864 最大报销额

    做DP一定要注意数组的大小,嗯,就是这样~ Description 现有一笔经费可以报销一定额度的发票.允许报销的发票类型包括买图书(A类).文具(B类).差旅(C类),要求每张发票的总额不得超过10 ...

  6. [SinGuLaRiTy] 树形DP专项测试

    [SinGuLaRiTy-1015] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 对于所有的题目:Time Limit:1s  |  Me ...

  7. dp周训练 状态压缩

    题目链接:题意:给你一个10*10的矩阵,每到一个格子中都要拿一个0-9的数值,求从矩阵左上方走到右下方且必须0-9都经过,拿的数值和最小是多少: #include <iostream> ...

  8. 1月18日 LCA专项训练

    A. Lorenzo Von Matterhorn B.Minimum spanning tree for each edge C.Misha, Grisha and Underground D.Fo ...

  9. Emiya家今天的饭 NOIP2019 (CSP?) 类DP好题 luoguP5664

    luogu题目传送门! 首先,硬求可行方案数并不现实,因为不好求(去年考场就这么挂的,虽然那时候比现在更蒟). 在硬搞可行方案数不行之后,对题目要求的目标进行转换: 可行方案数 = 总方案数 - 不合 ...

随机推荐

  1. 使用GitHub API上传文件及GitHub做图床

    本文介绍GitHub API基础及上传文件到仓库API,并应用API将GitHub作为图床 GitHub API官方页面 GitHub API版本 当前版本为v3,官方推荐在请求头中显示添加版本标识. ...

  2. Fiddler的一系列学习瞎记3

    Http: 不安全.可以很容易被拦截,或者其他的嗅探工具发现.怎么样做到安全?起码一下两点: 1.浏览器和we服务器之间的内容应该只有浏览器和web服务器能看到通信内容. 2.Http请求和Http的 ...

  3. 企业级工作流解决方案(十一)--集成Abp和ng-alain--权限系统服务

    权限系统主要定义为管理员增删改查权限数据,直接读取数据库,权限系统服务主要定义为供其他系统调用的权限验证接口,定义为两个不同的微服务. 权限系统有一个特点,数据变动比较小,数据量本身并不是很大,访问量 ...

  4. java开发两年,连Spring中bean的装配都不知道?你怎么涨薪啊

    Spring 1.1.1.1 创建一个bean package com.zt.spring; public class MyBean { private String userName; privat ...

  5. 面试必看!凭借着这份 MySQL 高频面试题,我拿到了京东,字节的offer!

    前言 本文主要受众为开发人员,所以不涉及到MySQL的服务部署等操作,且内容较多,大家准备好耐心和瓜子矿泉水. 前一阵系统的学习了一下MySQL,也有一些实际操作经验,偶然看到一篇和MySQL相关的面 ...

  6. 用Camtasia设计微课视频封面,让课程更加高大上

    在网络时代,尤其现在疫情影响只能线上学习,微课的应用前景已经越来越广了.但是想把微课做好,只有内容与录制精细是不够的,还需要一个精美的封面来吸引学生.接下来,小编就用微课制作软件Camtasia 20 ...

  7. Gradle全局代理配置

    配置文件路径:C:\Users\myName\.gradle\gradle.properties 代理配置内容: systemProp.http.proxyHost=127.0.0.1 systemP ...

  8. idea中快速将类中的属性转为Json字符串的插件

    当我们想要测试接口的时候,难免会根据一个类,一个一个的写json数据,当属性比较少时还行,但当属性多的时候就比较麻烦了, 为了解决这个问题,我们可以安装第三方的插件来快速生成json字符串. 步骤如下 ...

  9. Django踩坑记录3

    路径如下: admin.py的代码: from django.contrib import admin from sign.models import Event,Guest # Register y ...

  10. python连接mysql循环插入千万条数据脚本

    之前都是在mysql的存储过程中插入数据,毕竟mysql语法函数有限,很多都有限制.突然想到学了python正好可以练练手.首先需要安装pymysql模块包(模块包安装请自行百度) pip insta ...