【偽暴力+智商剪枝】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