位數差(整體二分、分治)
阿新 • • 發佈:2020-07-04
連結:https://ac.nowcoder.com/acm/problem/14380
來源:牛客網
空間限制:C/C++ 65536K,其他語言131072K
64bit IO Format: %lld
題目描述
給一個數組{a},定義h(a,b)為在十進位制下a + b與a的位數差,求,0的位數為1。輸入描述:
第一行讀入一個正整數 n (1 <= n <= 105)。
第二行讀入 n 個非負整數,第 i 個表示a[i] (0 <= a[i] <= 108)。
輸出描述:
一行表示答案。
示例1輸入
10 01 2 3 4 5 6 7 8 9
輸出
20
我們可以考慮整體二分(分治),將大問題分解成為小問題。二分割槽間長度,將L~R的問題化為L~mid、mid+1~R的問題,分治每個區間的數位差貢獻,再合併進行求解。合併計算時,對於當前左右區間,還要加上a[i]在左邊,a[k]在右邊的情況。具體可以對右區間進行排序,遍歷左區間的ai進行求在右區間符合條件的aj個數即可。
感覺這種區間題目好像都可以用分治去思考解法。對於給出的區間,直接二重for列舉很顯然會TLE掉,那麼我們看能不能去降降時間複雜度,假設[1, n]這個區間我們存在一個分治函式,可以先分開遞迴求[1, mid],[mid+1, n]區間的答案,那麼最終的答案就還要加上a[i]在左區間,a[j]在右區間的情況即可。因為我們只用去右邊匹配存在進位的個數即可,這樣就可以對右邊元素排序,直接二分查詢就可了。
因為關係到一個要進位,我們想對於ai假設其為一位數,那麼他想加上一個aj變成兩位數至少要加上10-ai才可以。這啟發我們可以把這個問題轉化為搜尋問題。對mid+1到R排個序,然後在mid+1到R之間使二分搜尋lower_bound(),查詢10-ai,那麼能夠使得ai升上兩位及兩位為以上aj數目即為對ans的貢獻。同理對於100,1000,10000,一直到1e9也是一樣的。
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 #define pb push_back 4 #define mst(a) memset(a,0,sizeof(a)) 5const int INF = 0x3f3f3f3f; 6 const double eps = 1e-8; 7 const int mod = 1e9+7; 8 const int maxn = 1e5+10; 9 using namespace std; 10 11 int a[maxn]; 12 LL jz[]={10,100,1000,10000,100000,1000000,10000000,100000000,1000000000}; 13 14 LL solve(int L,int R) 15 { 16 if(L==R) return 0; 17 int mid = (L+R)/2; 18 LL res = solve(L,mid) + solve(mid+1,R); //分治 19 sort(a+mid+1, a+R+1, less<int>()); 20 for(int i=L;i<=mid;i++) // 問題合併 21 { 22 for(int j=0;j<9;j++) 23 { 24 if(jz[j] > a[i]) //注意沒有= 25 res += a+R+1 - lower_bound(a+mid+1,a+R+1,jz[j]-a[i]);//加上滿足的個數 26 } 27 } 28 return res; 29 } 30 31 int main() 32 { 33 #ifdef DEBUG 34 freopen("sample.txt","r",stdin); //freopen("data.out", "w", stdout); 35 #endif 36 37 int n; 38 scanf("%d",&n); 39 for(int i=1;i<=n;i++) 40 scanf("%d",&a[i]); 41 printf("%lld\n",solve(1,n)); 42 return 0; 43 }
-