洛谷P1982 小朋友的数字——题解

题目传送

简单地说,这题就是让我们求前i个数的最大子串和和最值。

对于最大子串和,我们可以设一个变量qian,表示以当前元素结尾的最大子串的子串和。若搜索完第i-1个小朋友,现在看到第i个小朋友时,若qian大于0,就说明以第i-1个小朋友为结尾的最大子串和的值大于0,那么让这小朋友连上这个字串的话得到的子串和一定比让这个小朋友独自一人组成一个子串得到的和要大,而且在这时第i个小朋友连上这个字串后得到的这个子串也是以第i个小朋友为结尾的和最大的子串;若qian小于0,那第i个小朋友就不如自己单独组成一个新的子串,这样得来的子串和还要比让这个小朋友连上之前的子串得到的子串和要大。若qian大于目前搜索到的所有子串的和中最大的和maxn还要大,就更细maxn。第i个小朋友的特征值就是此时的maxn(注意,以第i个小朋友为结尾的子串不一定是前i个小朋友中组成的所有子串中子串和最大的子串)。

对于最值,我们要每次求第i个小朋友的分数时都把前面的小朋友分数加特征值都扫一遍吗?不用。我们只要维护一个表示当前的前i-1个小朋友中最大的分数与特征值的和的变量maxx就行了。

根据题目范围,显然小朋友的“手牌值”和特征值都是在long long 类型以内的。对于分数也好像在long long 以内。但真的是这样吗?

我们使单位“1”等于109(即小朋友最大可能的手牌值),看一下数据最大的情况,即有1000000个小朋友,每个小朋友手里的牌值都是一个“单位‘1’”,列有下表:

小朋友编号 1 2 3 4 5 6 7 8 9 ... 1,000,000
手牌值(1:109 1 1 1 1 1 1 1 1 1 ... 1
特征值(1:109 1 2 3 4 5 6 7 8 9 ... 1,000,000
分数(1:109 1 2 4 7 11 16 22 29 37 ... 499999500001

499999500001*109=4.99999500001*1020,而long long的数据最大约9*1018,显然会爆,怎么办?这里是用两个long long 型high和low用1015进制表示一个数,即任意一个不超过题意数据范围内的数都可以用high*base+low表示(base=1015)。一个普通的数x转成这样一个数,只需high=x/base,low=x%base。相加的话注意进位就好。对于符号,我们只要保证high和low同号或high为0就行。同时发现,因为low的绝对值小于base,那么如果low为负数,加上一个base就变成正数了;如果low为正数,减去一个base就变成负数了。

见AC代码:

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cctype>
 5 
 6 using namespace std;
 7 
 8 long long spe[1000001],num[1000001],ans,mod,qian ;
 9  
10 const long long base=1000000000000000;
11  
12 char ch;
13 
14 bool f;
15 
16 inline long long read()
17 {
18     ans=0;
19     f=0;
20     ch=getchar();
21     while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
22     while(isdigit(ch)) ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
23     return f?-ans:ans;
24 }
25 
26 struct lll{
27     long long high,low;
28 }poi[1000001],maxx;
29 
30 inline lll operator+(lll a,const lll &b)
31 {
32     a.low+=b.low;
33     a.high+=b.high+a.low/base;
34     a.low%=base;
35     if(a.high>0&&a.low<0) a.high--,a.low+=base;
36     if(a.high<0&&a.low>0) a.high++,a.low-=base;
37     return a;
38 }
39 
40 inline lll operator+(lll a,const long long c)
41 {
42     lll b;
43     b.high=c/base;
44     b.low=c%base;
45     a.low+=b.low;
46     a.high+=b.high+a.low/base;
47     a.low%=base;
48     if(a.high>0&&a.low<0) a.high--,a.low+=base;
49     if(a.high<0&&a.low>0) a.high++,a.low-=base;
50     return a;
51 }
52 
53 inline lll max(lll a,lll b)
54 {
55     if(a.high>b.high) return a;
56     if(a.high<b.high) return b;
57     if(a.low>=b.low) return a;
58     else return b;
59 }
60 
61 int main()
62 {
63     int n=read(),mod=read();
64     for(int i=1;i<=n;i++) num[i]=read();
65     long long maxn=num[1];
66     for(int i=1;i<=n;i++)
67     {
68         if(qian<0) qian=0;
69         qian+=num[i];
70         maxn=max(maxn,qian);
71         spe[i]=maxn;
72     }
73     poi[1].low=spe[1]%base;
74     poi[1].high=spe[1]/base;
75     maxx=poi[1]+spe[1];
76     for(int i=2;i<=n;i++)
77     {
78         poi[i]=maxx;
79         maxx=max(poi[i]+spe[i],maxx);
80     }
81     maxx=poi[1];
82     for(int i=2;i<=n;i++)
83         maxx=max(maxx,poi[i]);
84     cout<<(base%mod*maxx.high+maxx.low)%mod;
85     return 0;
86 }

 

有人说难题都是一个个简单的题叠加成的。对于很多题,其实我们只要把它分解成一个个我们能解决的简单的子问题,最后合并一下就行了。

posted @ 2019-06-19 09:36  千叶繁华  阅读(344)  评论(0编辑  收藏  举报