Sue的小球

名次排序问题

方块消除

奥运物流

Sue的小球

题目大意

有 \(n\) 个小球在下落,初始位置 \((x_i,y_i)\),下落速度为 \(v_i\)。你初始位置在 \(x_0\),速度为 \(1\)。我们定义一个小球的贡献为接到它的时候它的纵坐标。问接到每个小球(接只接一次)的贡献之和的最大值。

\(n\le 10^3\)。

思路

算是为dp开了另外一种思路。

首先你发现,我们贪心一下,肯定每次选没选且最靠近当前位置的小球,具体证明不是很难。形式化一下,就是说按横坐标排序之后,假设已经接到 \(l\sim r\),那么下次一定接 \(l-1\) 或者 \(r+1\)。

于是,我们可以设 \(dp_{l,r,0/1}\) 表示接到 \(l\sim r\) 这段区间当前是在 \(l/r\) 时的小球的最大贡献。然后你发现这样很合理,但是你不好进行转移,因为你不能算出当前的时间。

但是转念一想,我们发现,如果我们能够在计算每个小球就把当前没有接到的小球消失的贡献都去掉的话就没有这种问题了。于是我们可以设 \(w_{l,r}=\sum_{i=1}^n v_i-\sum_{i=l}^{r}v_i\),那么我们可以得到转移式:

\[dp_{l,r,0}\to y_l+\max\{dp_{l+1,r,0}-w_{l+1,r}\times (x_{l+1}-x_l),dp_{l+1,r,1}-w_{l+1,r}\times (x_r-x_l)\}
\]

\(dp_{l,r,1}\) 同理。时间复杂度 \(\Theta(n^2)\)。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define MAXN 1005 struct node{
int x,y,v;
bool operator < (const node &p)const{return x < p.x;}
}fuck[MAXN]; int n,x0,w[MAXN][MAXN],f1[MAXN][MAXN],f2[MAXN][MAXN]; template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int Abs (int x){return x > 0 ? x : -x;} signed main(){
read (n,x0);int tot = 0;
for (Int i = 1;i <= n;++ i) read (fuck[i].x);
for (Int i = 1;i <= n;++ i) read (fuck[i].y);
for (Int i = 1;i <= n;++ i) read (fuck[i].v),tot += fuck[i].v;
sort (fuck + 1,fuck + n + 1);
memset (f1,0xcf,sizeof (f1));
memset (f2,0xcf,sizeof (f2));
for (Int i = 1;i <= n;++ i)
for (Int j = i,sum = 0;j <= n;++ j) sum += fuck[j].v,w[i][j] = tot - sum;
for (Int i = 1;i <= n;++ i) f1[i][i] = f2[i][i] = fuck[i].y - Abs (x0 - fuck[i].x) * tot;
for (Int i = n;i >= 1;-- i)
for (Int j = i + 1;j <= n;++ j)
f1[i][j] = max (f1[i][j],fuck[i].y + f1[i + 1][j] - (fuck[i + 1].x - fuck[i].x) * w[i + 1][j]),
f1[i][j] = max (f1[i][j],fuck[i].y + f2[i + 1][j] - (fuck[j].x - fuck[i].x) * w[i + 1][j]),
f2[i][j] = max (f2[i][j],fuck[j].y + f1[i][j - 1] - (fuck[j].x - fuck[i].x) * w[i][j - 1]),
f2[i][j] = max (f2[i][j],fuck[j].y + f2[i][j - 1] - (fuck[j].x - fuck[j - 1].x) * w[i][j - 1]);
printf ("%.3f\n",max (f1[1][n],f2[1][n]) / 1000.0);
return 0;
}

名次排序问题

题目大意

给出一个 \(n\) 个点的序列 \(a_{1,2,...,n}\),每次可以把 \(i\) 换到 \(j\),贡献是 \(i+j\) ,问最后变成一个单调下降序列的最小贡献时的交换次数。

\(n\le 10^3\),保证 \(a_i\) 互不相等。

思路

论文

luogu上没有,如果有的话一定是个黑题吧。

首先我们可以转移题意,转换成一个权值 \(\in [1,n]\) 的序列,变为 \(1\sim n\) 的最小贡献。

然后你发现这个东西有以下 \(2\) 个性质:

  • 每个点只会交换一次

  • 交换一定权值从大到小进行

第一个显然,第二个可以靠猜(反正我猜中了)或者感性理解。

于是我们得到一个几个推论:

  1. 对于每一个数 \(x\),你在移动时有两种选择,1、 移动到数 \(x+1\) 前面,或者如果 \(x\) 的位置小于 \(x+1\) 的位置,那么可以不移动,等待后面 \(x\) 到 \(x+1\) 之间的数都移动走。

  2. 对于每一个数 \(x\),比它小的数相对位置不变。(相对位置就是指谁在前谁在后)

  3. 对于每一个数 \(x\),比 \(x+1\) 大的数都一定在 \(x+1\) 后面。

后面 2 个推论可以从第 1 个推出来,这里就不讲了。(因为跟主旨无关(是不是该夸夸我详略得当(伦敦雾

但是我们发现我们光知道相对位置没有什么用啊,我们得知道确切位置才可以啊。其实知道相对位置是做得到的。

我们发现对于一个数 \(x\) ,如果 \(x+1\sim n\) 都移动完了,那么当前位置其实就是 \(1\sim pos_x\) 中比 \(x\) 小的数 \(+1\) ,其中 \(pos_x\) 就是 \(x\) 在原序列的位置。这个可以从推论 2 看出来。

然后你发现还是不好搞,于是我们修改一下规则,把移动 \(i\to j\) 变为 \(i\) 加入 \(j\) 所在的点。然后你发现这样做的话,你就不会移动点了,而且点之间的相对位置也不会改变。(具体例子可以看论文)

接着我们设 \(c(p,x)\) 表示原序列中 \(1\sim p\) 中比 \(x\) 小的数 \(+1\)。然后我们发现如果 \(pos_i<j\) ,把 \(i\) 移动到 \(j\) 的贡献其实就是 \(c(pos_i,i)+c(j,i+1)-1\)(\(-1\) 是因为移动到 \(j-1\) 前面)。\(pos_i>j\) 同理。

于是我们考虑设 \(dp_{i,j}\) 表示已经考虑了 \(i+1\sim n\),且点 \(i\) 移动到 \(j\) 的最小贡献。对于第1中选择,转移式就是:

\[dp_{i,j}=dp_{i+1,j}+c(pos_i,i)+1+c(j,i)+1
\]

这里的式子跟上面不一样是因为这个式子归纳了 \(pos_i>j\) 的情况。

我们还需要考虑第2中情况,即它不变时的方案。注意到这篇论文的内容 我们发现我们需要考虑当前不动对后面的影响。你发现对于中间( \(x\) 和移动到的点之间)未考虑的点 \(i\) 它一定会移动到 \(x\) 的前面去,那么轮到 \(i\) 的时候 \(x\) 的前面已经排了 \(x-1,x-2,...,i+1\),所以它还需要额外算上 \(x-i\),剩下的部分会在上面考虑到。对于移动到的点之后的点肯定在轮到移动到的点的时候就考虑到了。

于是转移式就是:

\[dp_{i,pos_i}=\min\{dp_{i+1,j}+\sum_{k=i+1}^{j-1}[a_k<i](i-a_k)\}
\]

(\(a_k\) 是序列第 \(k\) 个)

于是我们就可以在 \(\Theta(n^2)\) 的时间复杂度内解决这个问题了。

不过我们要求最后的答案的话我们需要统计一下每一个点移动的点,具体可以见代码。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define INF 0x7f7f7f7f
#define MAXN 1005 int n,tot,pos[MAXN],dp[MAXN][MAXN],pre[MAXN]; struct node{
int x,ind;
}fuck[MAXN]; bool cmp1 (node a,node b){return a.x > b.x;}
bool cmp2 (node a,node b){return a.ind < b.ind;} template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} void dfs (int i,int j){
if (i == n) return ;
if (pos[i] != j) tot ++,dfs (i + 1,j);
else dfs (i + 1,pre[i]);
} signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (fuck[i].x),fuck[i].ind = i;
sort (fuck + 1,fuck + n + 1,cmp1);
for (Int i = 1;i <= n;++ i) pos[i] = fuck[i].ind,fuck[i].x = i;
sort (fuck + 1,fuck + n + 1,cmp2);
memset (dp,0x7f,sizeof (dp));
++ n,dp[n][n] = 0,fuck[n].x = n;
for (Int i = n - 1;i;-- i){
int v1 = 1,v2 = 1;
for (Int j = 1;j < pos[i];++ j) v1 += (fuck[j].x < i);
for (Int j = 1;j <= n;++ j) v2 += (fuck[j].x < i),dp[i][j] = min (dp[i][j],dp[i + 1][j] + v1 + v2);
for (Int j = pos[i] + 1,tmp = 0;j <= n;++ j){
if (dp[i + 1][j] + tmp < dp[i][pos[i]]) dp[i][pos[i]] = dp[i + 1][j] + tmp,pre[i] = j;
if (fuck[j].x < i) tmp += i - fuck[j].x;
}
}
int ans = 0x7f7f7f7f,pos = 0;
for (Int i = 1;i <= n;++ i) if (dp[1][i] < ans) ans = dp[1][i],pos = i;
dfs (1,pos),write (tot),putchar ('\n');
return 0;
}

参考博客

https://www.cnblogs.com/yimmortal/p/10160630.html 建议与论文一起服用

https://blog.csdn.net/icefox_zhx/article/details/80014541 可以看一下代码实现(或者看我的也可以)

方块消除

题目大意

有一个数列,每次可以消掉一掉连续的颜色相同的一块,贡献就是 \(x^2\),其中 \(x\) 是长度。问最大贡献。

思路

不知道如何去讲思考过程,所以直接讲方法?

我们可以设 \(f_{l,r,i}\) 表示消掉 \(l,r\) 并且假设在 \(r\) 的右边存在一个长度为 \(i\) 并颜色与 \(r\) 相同的块的最大贡献。于是我们可以得到转移式:

\[f_{l,r,i}=f_{l,r-1,0}+(len_r+i)^2
\]
\[f_{l,r,i}\to f_{l,j,i+len_r}+f_{j+1,r-1,0}
\]

显然,想一下就明白了。最后答案就是 \(f_{1,n,0}\)

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define Int register int
#define MAXN 205 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int sqr (int x){return x * x;}
int n,col[MAXN],pre[MAXN],len[MAXN],dp[MAXN][MAXN][MAXN]; signed main(){
read (n);
for (Int i = 1;i <= n;++ i) read (col[i]);
for (Int i = 1;i <= n;++ i) read (len[i]),pre[i] = pre[i - 1] + len[i];
for (Int leng = 1;leng <= n;++ leng){
for (Int l = 1;l + leng - 1 <= n;++ l){
int r = l + leng - 1;
for (Int i = 0;i <= pre[n] - pre[r];++ i) dp[l][r][i] = dp[l][r - 1][0] + sqr (len[r] + i);
for (Int i = l;i <= r - 1;++ i)
for (Int j = 0;j <= pre[n] - pre[r];++ j)
if (col[i] == col[r]) dp[l][r][j] = max (dp[l][r][j],dp[l][i][len[r] + j] + dp[i + 1][r - 1][0]);
}
}
write (dp[1][n][0]),putchar ('\n');
return 0;
}

奥运物流

题目大意

有一个 \(n\) 个点的图,给出参数 \(k\),每个点有一个后继,每个点有两个参数,分别为 \(R(x),C(x)\),其中 \(C(x)\) 是给定参数,\(R(x)\) 由 \(C(x)\) 计算,公式为:

\[R(x)=C(x)+k\sum_{v\in son_u}R(v)
\]

现在你有 \(m\) 次修改机会,每次可以更改一个点的后继。求 \(R(1)\) 的最大值。

思路

下面定义 \(d(i)\) 表示 \(i\to 1\) 的距离。

首先我们发现这个图一定是一个森林,就是很多棵树,然后树的根构成一个环。

我们发现对于对于一个点 \(x\),他对 \(R(1)\) 的贡献就是 \(C(x)\times k^{d(x)}/(1-k^{len})\),其中 \(len\) 是环上点的数量。具体说明可以分类讨论,分别考虑环上的点以及树上的点来说明。

然后我们如果枚举环的长度的话,那么我们就只需要让 \(\sum_{x=1}^{n}C(x)\times k^{d(x)}\) 最大。这个问题就类似于没有上司的舞会。

我们考虑设计状态 \(dp_{u,i,d}\) 表示以 \(u\) 为根的子树改变了 \(i\) 次,点 \(i\) 深度为 \(d\) 的最大贡献。于是,我们可以得到转移式:

\[dp_{u,i,d}\to dp_{u,j,d}+\max\{dp_{v,i-j,d+1}+dp_{v,i-j-1,1}\}
\]

转移显然。

最后再把环上的点都合并起来就行了。在实现的时候为了方便我们可以直接破环,把整个图变成一棵树,以 \(1\) 为根做上述 \(\text{dp}\) 即可。

时间复杂度最坏 \(\Theta(n^5)\) (假设 \(n,m\) 同阶)

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define double long double
#define Int register int
#define MAXN 65 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,m,s[MAXN];
vector <int> G[MAXN];
double k,c[MAXN],pw[MAXN],tmp[MAXN][MAXN],dp[MAXN][MAXN][MAXN]; void dfs (int u,int dep,int up){
for (Int i = 0;i <= up;++ i) for (Int j = 0;j <= dep;++ j) dp[u][i][j] = c[u] * pw[j];
for (Int v : G[u]){
dfs (v,dep + 1,up);
memset (tmp,0,sizeof (tmp));
for (Int i = 0;i <= up;++ i)
for (Int j = 0;i + j <= up;++ j)
for (Int d = 0;d <= dep;++ d){
tmp[i + j][d] = max (tmp[i + j][d],dp[u][i][d] + dp[v][j][d + 1]);
if (j > 0) tmp[i + j][d] = max (tmp[i + j][d],dp[u][i][d] + dp[v][j - 1][1]);
}
for (Int i = 0;i <= up;++ i) for (Int j = 0;j <= dep;++ j) dp[u][i][j] = tmp[i][j];
}
} double calc (int up){
dfs (1,0,up);
return dp[1][up][0];
} signed main(){
read (n,m);scanf ("%Lf",&k);
pw[0] = 1;for (Int i = 1;i <= n;++ i) pw[i] = pw[i - 1] * k;
for (Int i = 1;i <= n;++ i) read (s[i]);
for (Int i = 1;i <= n;++ i) scanf ("%Lf",&c[i]);
double ans = 0;
for (Int d = 2,p = s[1];p != 1;++ d,p = s[p]){
for (Int i = 1;i <= n;++ i) G[i].clear ();
int t = s[p];s[p] = 1;
for (Int i = 2;i <= n;++ i) G[s[i]].push_back (i);
ans = max (ans,calc (m - (t != 1)) / (1 - pw[d]));
s[p] = t;
}
printf ("%.2Lf\n",ans);
return 0;
}

题解 Sue的小球/名次排序问题/方块消除/奥运物流的更多相关文章

  1. BZOJ-2037 Sue的小球 DP+费用提前

    似乎很早时学长考过很类似的? 2037: [Sdoi2008]Sue的小球 Time Limit: 10 Sec Memory Limit: 64 MB Submit: 558 Solved: 300 ...

  2. 【BZOJ2037】Sue的小球(动态规划)

    [BZOJ2037]Sue的小球(动态规划) 题面 BZOJ 题解 莫名想到这道题目 很明显是一样的 设\(f[i][j][0/1]\)表示已经接到了\(i-j\)这一段的小球 当前在\(i\)或者在 ...

  3. 【BZOJ2037】[Sdoi2008]Sue的小球 区间DP+费用提前

    [BZOJ2037][Sdoi2008]Sue的小球 Description Sue和Sandy最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue有一支轻便小巧的小船.然而 ...

  4. 2037: [Sdoi2008]Sue的小球

    2037: [Sdoi2008]Sue的小球 链接 题解 论文 代码 #include<cstdio> #include<algorithm> #include<cstr ...

  5. [Luogu2135] 方块消除【区间Dp】

    Online Judge:P2135 方块消除(这题不用预处理) Label:区间Dp 题目描述 Jimmy最近迷上了一款叫做方块消除的游戏.游戏规则如下:n个带颜色方格排成一列,相同颜色的方块连成一 ...

  6. 第八讲:HTML5中canvas实现小球击打小方块游戏

    源码:http://download.csdn.net/detail/liumingm900913/7469969 游戏开发流程: 1.创建画布: 将画布放在div标签里面,这样能够控制画布居中的位置 ...

  7. [BNUZOJ1261][ACM][2016北理校赛]方块消除(栈,字符串)

    玩过方块消除游戏吗?现在规定当有两个或两个以上相邻且颜色相同的方块在一起的时候,它们就会产生消除反应.当存在多个消除反应同时产生时,最下的反应先执行.现在只给你其中一列,求最后剩下的方块结果. 输入要 ...

  8. 洛谷 P2466 Sue的小球 解题报告

    P2466 [SDOI2008]Sue的小球 题目描述 Sue和Sandy最近迷上了一个电脑游戏,这个游戏的故事发在美丽神秘并且充满刺激的大海上,Sue有一支轻便小巧的小船.然而,Sue的目标并不是当 ...

  9. P2135 方块消除

    题目描述 Jimmy最近迷上了一款叫做方块消除的游戏.游戏规则如下:n个带颜色方格排成一列,相同颜色的方块连成一个区域(如果两个相邻方块颜色相同,则这两个方块属于同一区域).为简化题目,将连起来的同一 ...

随机推荐

  1. java的stream的使用

    过滤 filter: //匹配第一个元素 Optional<Integer> findFirst=list.stream().filter(x->x>6).findFirst( ...

  2. mybatis插值,数据提交事务回滚数据库值为空

    mybatis插值,数据提交事务回滚数据库值为空 通过sql日志查看sql为:INSERT INTO `quanxian`.`user` ( phone, email, password, times ...

  3. JS中原型与原型链

    一. 普通对象与函数对象 JavaScript 中,万物皆对象!但对象也是有区别的.分为普通对象和函数对象,Object .Function等 是 JS 自带的函数对象.下面举例说明. var o1 ...

  4. Linux下chkconfig

    1.chkconfig命令用于检查,设置系统的各种服务! 2.chkconfig语法 chkconfig [--add] [--del] [--list] [系统服务] 或 chkconfig [-- ...

  5. Qt+Python开发百度图片下载器

    一.资源下载地址 https://www.aliyundrive.com/s/jBU2wBS8poH 本项目路径:项目->收费->百度图片下载器(可试用5分钟) 安装包直接下载地址:htt ...

  6. DFS与DFS迷宫问题

    一天蒜头君掉进了一个迷宫里面,蒜头君想逃出去,可怜的蒜头君连迷宫是否有能逃出去的路都不知道. 看在蒜头君这么可怜的份上,就请聪明的你告诉蒜头君是否有可以逃出去的路. 输入格式 第一行输入两个整数n 和 ...

  7. C++ 输入一行未知个数的整数

    C++ 输入一行未知个数的整数 代码: #include<iostream> #include<vector> using namespace std; int main() ...

  8. PTA——c++面向对象基础

    1.结构不是面向对象的主要特征 2.每个 C++程序中都必须包含有这样一个函数,该函数的函数名为main 3.C++对C语言作了很多改进,下列描述中()使得C语言发生了质变,从面向过程变成了面向对象. ...

  9. vue-自定义指令(directive )的使用方法

    前言 在vue项目中我们经常使用到 v-show ,v-if,v-for等内置的指令,除此之外vue还提供了非常方便的自定义指令,供我们对普通的dom元素进行底层的操作.使我们的日常开发变得更加方便快 ...

  10. 让PHP能够调用C的函数-FFI扩展

    在大型公司中,一般会有很我编程语言的配合.比如说让 Java 来做微服务层,用 C++ 来进行底层运算,用 PHP 来做中间层,最后使用 JS 展现效果.这些语言间的配合大部分都是通过 RPC 来完成 ...