题目:

写这题花了我一上午时间。

下面是本人(zhangjiuding)的思考过程:

首先想到的是三行,每一行一定要走到。

大概是这样一张图

每一行长度最少为1。即第一行(i -1) >= 1,第二行 (j - i) >= 1,第三行 (n - j) >= 1。

我们要求的就是这条路径上的和。

我们发现只要 i 和 j 固定了,结果就固定了。

如果每次都要重新求和的话时间复杂度肯定不够,所以我这个时候想到了前缀和。

用sum[a][b]表示第a行(总共只有3行)前b个数字的和。

第一行可以用 sum[1][i]来表示,第二行可以用sum[2][j]-sum[2][i-1]来表示,第三行可以用sum[3][n]-sum[3][j-1]来表示。

(此处用到了i-1所以我们吧sum[][0]空出来,这样就不会越界)

ans = sum[1][i] + sum[2][j] - sum[2][i-1] + sum[3][n] - sum[3][j-1];

每一组i、j都有一个固定结果,我们求出其中取模最大的那个就好了。

现在求和的问题解决了,该固定i , j 了。

下面看到i的取值范围(1 <= i <= n),j的取值范围(i <= j <= n)

如果遍历i和j的话时间复杂度还是O(n^2),所以前缀和+暴力肯定是会超时的。

如果能优化一点到O(n*log(n))就好了,这样就不会超时了。

那么能优化吗?答案是肯定的。

我们先把我们的每一对i,j对应的ans分离一下。

定义up = sum[1][i] - sum[2][i-1];

  down = sum[2][j] + sum[3][n] - sum[3][j-1];

这样up中只含有i,down中只含有j。

因为我们知道结果只和 i , j 有关,

如果我们能把每一个up可能的down存起来,二分法找出那个能让结果最大的down值,然后用up+down更新ans就好了。

这样时间复杂度就只有O(n*log(n))了。

你可能看到这里还有疑问,先怀着疑问,后面我会把我碰到的问题和疑问都讲出来。

先看代码有助于理解:

#include <bits\stdc++.h>
using namespace std;
typedef long long ll; //定义结构体,value存输入的值,sum存当前行的前缀和。
struct node{
ll value;ll sum;
}a[][];
//ifstream in("in15.txt"); int main(){
int n;
ll p;
cin >> n >> p;
for(int i = ;i <= ; i++){
for(int j = ;j <= n; j++){
cin >> a[i][j].value;
a[i][j].sum = a[i][j].value+a[i][j-].sum; // 求前缀和。
}
} ll ans = ;
set<ll> s; // 用set存当前i值可能碰到的j值对应的down。
set<ll>::iterator it; //反向遍历是因为down的范围可以慢慢扩张,不用删除set中的元素
for(int i = n; i >= ; i--){
ll up = (a[][i].sum-a[][i-].sum)%p; // 当前i值对应的up。
up = (up+p)%p; // 强行变成正数。
ll down = (a[][i].sum+a[][n].sum-a[][i-].sum)%p; // 当前i值对应的down。
down = (down+p)%p; // 强行变成正数。
// cout << "i:" << i << endl;
// cout << "up:"<< up << endl;
// cout << "down:" << down << endl; s.insert(down);//因为肯定是j >= i的,所以每次把当前i值对应的down加入到set,set就会表示可能取到的所有的j对应的down的集合。 //明确一点,set里装的全是down。 // 二分查找,找到一个down值与up相加后最大。
it = s.lower_bound(p-up); // 找到第一个大于等于p-up的down值,我们需要将it--,求出第一个比p-up小的。 //注意,1、2顺序不能变。
if(it != s.begin()) { //1.如果等于it = s.begin(),因为it还要减一下,相当于没有找到合适的值。
it--; // 2.it--,让it指向第一个比p-up小的值。
ans = max(ans,(up+*it)%p); // 更新 ans。
} //可能第一个比p-up小的down值加上up后还是比较小,反而down的最大值加上up后取模还比较大,所以话线性时间更新一下ans。
it = s.end();
it--;
ans = max(ans,(*it+up)%p);
}
cout << ans << endl;
return ;
}
//writed by zhangjiuding

看到这里可能有一个最大的问题,那就是强行变正数的问题。

比如p = 10, up求出来的是-3,而down求出来是6,(-3 + 6)%10 = 3还是正数,强行将up变成正数是否会出错呢?

答案是肯定不会,   up 强行变正后是7,(7+6)% 10 = 3,还是3。

看起来逻辑性并不强,但是我试过了很多组数据都是一样的结果,强行变正并不影响结果。

我的猜测是:因为数组中所有的数都是正数或者0,所以每一个up+down一定会大于0。

      如果up小于0,那么它可以取到的所有的down都会使 up+down >= 0。

      只是这些值是取过模的,所以会出现负数,我们需要将它放在0~(p-1)之间。

51 nod 1624 取余最长路 思路:前缀和 + STL(set)二分查找的更多相关文章

  1. 1624 取余最长路(set)

    1624 取余最长路 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 佳佳有一个n*m的带权矩阵,她想从(1,1)出发走到(n,m)且只能往右往下移动,她能得到的娱 ...

  2. 51nod 1624 取余最长路

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1624 题意: 思路:因为一共只有3行,所以只需要确定第一行和第二行的转折 ...

  3. 51nod1624 取余最长路 前缀和 + set

    由于只有3行,因此只会会换行2次,假设$x, y$分别为这两次的换行点 那么答案为$S[1][x] +S[2][y] - S[2][x - 1] + S[3][n] - S[3][y - 1]$ 其中 ...

  4. 51nod 1624 取余最短路(set)

    题意: 佳佳有一个n*m的带权矩阵,她想从(1,1)出发走到(n,m)且只能往右往下移动,她能得到的娱乐值为所经过的位置的权的总和. 有一天,她被下了恶毒的诅咒,这个诅咒的作用是将她的娱乐值变为对p取 ...

  5. 51 nod 1097 拼成最小的数 思路:字符串排序

    题目: 思路:1.以字符串输入这些整数. 2.对这些字符串排序,排序规则为尽量让能让结果变小的靠前. 代码中有注释,不懂的欢迎在博客中评论问我. 代码: #include <bits\stdc+ ...

  6. 51 nod 1055 最长等差数列(dp)

    1055 最长等差数列 基准时间限制:2 秒 空间限制:262144 KB 分值: 80 难度:5级算法题 N个不同的正整数,找出由这些数组成的最长的等差数列.     例如:1 3 5 6 8 9 ...

  7. 51 nod 1427 文明 (并查集 + 树的直径)

    1427 文明 题目来源: CodeForces 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 难度:6级算法题   安德鲁在玩一个叫“文明”的游戏.大妈正在帮助他. 这个游 ...

  8. 题解报告:hihoCoder #1050 : 树中的最长路

    描述 上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中,小Ho发现他不仅仅可以拼凑成一棵二叉树!还可以拼凑成一棵多叉树——好吧,其实就是更为平常的树而已. 但 ...

  9. poj 3349:Snowflake Snow Snowflakes(哈希查找,求和取余法+拉链法)

    Snowflake Snow Snowflakes Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 30529   Accep ...

随机推荐

  1. static和final修饰方法

    static修饰的方法是静态方法,所有的对象共用一份,也就是共享方法.static方法是可以被继承,然后可以被重写和重载. final修饰的方法是不可变方法,final方法所在类被继承时,被final ...

  2. github如何搜索资料

    进入自己的主页,然后点击explore→trending; 或者google搜索,如github java 后端 请参考:http://mp.weixin.qq.com/s?__biz=MzA4NTQ ...

  3. C#中MessageBox.Show()方法详解

    1. // 摘要: // 显示具有指定文本的消息框. // // 参数: // text: // 要在消息框中显示的文本. // // 返回结果: // System.Windows.Forms.Di ...

  4. wcf 上传文件报413,404和发布错误

    上传文件错误: 其实要修改所有的服务,不管是服务端还是客户端,Binding那边增加一个没有设置名字的默认配置就OK了:  <binding   closeTimeout="00:10 ...

  5. C# 反射、与dynamic最佳组合

    在 C# 中反射技术应用广泛,至于什么是反射.........你如果不了解的话,请看下段说明,否则请跳过下段.广告一下:希望我文章的朋友请关注一下我的blog,这也有助于提高本人写作的动力. 反射:当 ...

  6. 【JCP模式实战--ferrous-framework】ferrous前端开发框架邀您初体验

    一.简介 ferrous-framework是为了迎合微服务架构而封装的纯前端开发框架. 实现了一种介于单页面和多页面的开发模式,让大家根据自己的需要对单页面和多页面进行切换或者共存. 页面结构采用J ...

  7. 分布式测试工具Beetle.DT的部署并进行HTTP,SQL,TCP压测

    由于Beetle.DT是一个分布式压力测试工具,所以在使用上并不像普通工具那样安装运行这么简单:由于工具涉及到测试管理中心,节点和管理端等工具: 所以必须要进行相应的部署才能运行.接下来详解一下如果安 ...

  8. 使用设置sa用户登录sql server2008

    今天在net项目中添加数据库过程中出现了小问题,就是使用sql server身份验证没登录成功,经过一番调试,终于解决问题. 使用sa账户登录sql server 2008 的方法步骤如下: 1.首先 ...

  9. 201521123089《Java程序设计》第6周学习总结

    1. 本周学习总结 2. 书面作业 clone方法1.1 Object对象中的clone方法是被protected修饰,在自定义的类中覆盖clone方法时需要注意什么?                 ...

  10. Java-错误处理机制学习(一)异常处理

    注意:本文介绍Java中的异常处理理论知识及相关语法结构,对于实际应用来说是万万不够的.关于如何高效地使用异常,请查看Java-高效地使用Exception-实践. 异常处理的思想是,当应用程序处于异 ...