1. 程式人生 > >【偽暴力+智商剪枝】Codeforces Round #489 (Div. 2) D

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

profile 接下來 時間 一個數 鏈表 數字 sum 方式 print

失蹤人口突然回歸……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