伪暴力+智商剪枝Codeforces Round #489 (Div. 2) D

Posted l-excalibur

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了伪暴力+智商剪枝Codeforces Round #489 (Div. 2) D相关的知识,希望对你有一定的参考价值。

失踪人口突然回归……orz。题解还是有必要写的,虽然估计只有自己(?自己也不一定看得懂)看得懂。

 

题目链接:http://codeforces.com/contest/992/problem/D

题目大意:给出n个数字a和一个k,求数列a中的子区间aa满足aa的和乘k等于aa的积。(a<=1e8,n<=2e5,k<=1e5)

这道题没找到官方题解,所以看了一下standing rank1的dalao的代码。 鸣谢 dotorya

第一眼就觉得短,非常短。赛上把我卡得很恶心的1的情况竟然被两行代码解决了……再度%大佬的机智。

暴力解决这道题(枚举所有区间)的时间复杂度是n^2,在看了一眼数据范围后被我放弃了。

先说一下赛上思路过程,前缀和是第一反应,然后考虑前缀积。再一看范围,计算量直接T掉高精度。不过转念一想,k*sum_aa的最大值也就2e18,好像是在暗示着什么。用和来寻找积而不用积来寻找和。在草稿本上演算了一下,发现积的增长速度十分快,一旦在某个瞬间大于sum_aa*k了之后就不会再小于了。当然是在没有1的情况。想了半天不知道怎么破解这个1,最终GG退赛。

看了dalao的代码之后发现dalao机智地把1折叠了起来……既然乘1等于不乘那我们就不乘,跳过就行了,加1的影响用前缀和搞定。然后……就是暴力,对,暴力(Formiko的智商真是碾压我呀。)

%%%,我初步估计时间复杂度在nlog2e18,因为乘积的增长速度真的十分十分快,log级别的。此处鸣谢Formiko点醒了我。这确实是基本的数学素质,我竟然忘掉了。

中途写完代码的时候发现不能完全忽略1的影响,再次感谢formiko,他的一句“1加着加着就蹦出来一个解”成功帮我AC了这道题,同时%dalao的二分思路。

废话太多我精简地说一下这道题的题解。

首先用前缀和处理数列,把这个数列映射到另一个数列上,类似于链表(但是要保留原下标,方便前缀和以及1的影响)。映射方式就是折叠1,只保留非1的数,也就是对乘积有影响的数,对于固定的左端点,这个区间长度不会超过63,时间复杂度是可以承受的。接下来暴力枚举每个左端点,在映射的数组上跳跃,最多条约63次。中途会有1出现的情况。也就是分右端点是1和右端点不是1两种情况。如果右端点不是1,那么直接在映射数组上验证是否满足条件就行了,如果右端点是1,那么在一段连续1中,由于乘积一直乘的1,值不变,和一直在增加,所以如果出现满足条件的解,只会有一个,而且满足单调性!!那么二分就可以了。总时间复杂度在63*nlogn。

下面放代码:

 

 1 /* by Lstg */
 2 
 3 #include<stdio.h>
 4 #include<iostream>
 5 #define MAXN 200105
 6 #define inf 3000000000000000000
 7 using namespace std;
 8 
 9 int b[MAXN];
10 long long a[MAXN],sum[MAXN];
11 
12 bool _find(int k,int l,int r,long long tmp){
13     
14     if(l>r)return false;
15     int mid;
16     while(l<=r){
17         mid=(l+r)>>1;
18         if(sum[mid]-sum[k]==tmp)return true;
19         if(sum[mid]-sum[k]>tmp)r=mid-1;
20         else l=mid+1;
21     }
22     return false;
23 }    
24     
25 
26 int main(){
27     
28     int n,i,j;
29     long long k,tmp;
30     scanf("%d%I64d",&n,&k);
31     for(i=1;i<=n;i++){
32         scanf("%I64d",&a[i]);
33         sum[i]=sum[i-1]+a[i];
34     }
35     b[i]=i;
36     for(i=n;i>=1;i--){
37         if(a[i]>1)b[i]=i;
38         else b[i]=b[i+1];
39     }
40     
41     long long cnt=0;
42     for(i=1;i<=n;i++){
43         j=b[i];
44         tmp=1ll;
45         if(a[i]==1&&k==1)cnt++;
46         while(j<=n){
47             if(inf/a[j]<tmp)break;
48             tmp*=a[j];
49             if(tmp==k*(sum[j]-sum[i-1]))cnt++;
50             if(tmp%k==0&&_find(i-1,j+1,b[j+1]-1,tmp/k))cnt++;
51             j=b[j+1];
52         }
53     }
54     printf("%I64d",cnt);
55     return 0;
56 }

 

以上是关于伪暴力+智商剪枝Codeforces Round #489 (Div. 2) D的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #439 (Div. 2) Problem A (Codeforces 869A) - 暴力

Codeforces Round #540 (Div. 3) C. Palindromic Matrix 暴力

Codeforces Round #439 (Div. 2) Problem E (Codeforces 869E) - 暴力 - 随机化 - 二维树状数组 - 差分

Codeforces Round #415 (Div. 2)(A,暴力,B,贪心,排序)

Codeforces Round #166 (Div. 2)暴力A. Beautiful Year

Codeforces Round #166 (Div. 2)暴力A. Beautiful Year