1. 程式人生 > >csu oj 1343 Long Long

csu oj 1343 Long Long

來看 ont 開始 描述 color 個數 sample nbsp for循環

Description

現在有兩個單調遞增序列,第一個序列有N個整數,第二個序列有M個整數,現在你可以從第一個序列中選一個數x,然後從第二個序列中選一個數y,那麽有多少種情況滿足x+y<=K呢?

Input

輸入包含多組數據。
對於每組測試數據,第一行包含兩個整數N, M , K (1 <= N, M <= 105, 1 <= K <= 109),含義同上。接下來一行包含N個在[1, 109]範圍內的整數,依次描述了第一個序列中的各個整數。再接下來一行包含M個在[1, 109]範圍內的整數,依次描述了第二個序列中的各個整數。

Output

對於每組數據,輸出有多少種情況滿足x + y <= K

Sample Input

2 3 5
3 7
3 5 7
4 5 6
1 2 3 4
1 2 3 4 5
4 5 9
1 2 3 4
1 2 3 4 5

Sample Output

0
14
20

Hint

其實如果兩個序列並不是有序的,我們也可以先通過排序使其變得有序,然後就和現在的這個題目一樣了,不過由於這個題目並不是為了介紹排序,所以幹脆就直接給出有序的序列了。
由於int的範圍有限,當要讀入的數或者要輸出的數超出了int的表示範圍但沒有超出long long的表示範圍時,我們就要使用long long了。如果OJ的服務器是Linux的,那麽用“%lld”(前面是兩個小寫的L)來控制long long的讀入和輸出,而如果OJ的服務器是Windows的,則需要用“%I64d”(前面是一個大寫的i)來控制long long的讀入和輸出,這一點一般會在OJ的“F.A.Q”有所說明的。由於我們的服務器是采用的Linux操作系統,所以使用“%lld”就OK了。
比如這個題目,極端情況就所有情況都滿足,也就是N*M中情況,當N和M都取極限值時,顯然結果就需要用long long來保存並輸出了。
那麽這個題目具體要怎麽做呢?
也許你一眼就看出來了,兩層for循環就可以搞定嘛,不過很可惜這樣估計要妥妥超時,要不試一下?
我們不妨仔細梳理一下思路,兩層for循環做法的基本思想應當是首先枚舉第一個序列中拿出的數x,然後再尋找第二個序列中有多少個整數y滿足x+y<=K。其實我們不難發現,每次只要找到滿足x+y<=K最大的y就可以了,不妨記為max_y,那麽max_y以及max_y之前的數是一定都滿足x+y<=K的,這樣根據max_y所在的位置,我們就能直接計算出對於當前枚舉到的x有多少個y滿足x+y<=K了。而找這個max_y的過程真的需要一層for循環嗎?你可能已經想到了,我們完全可以用“二分”的方法去找這個max_y,這樣就能快很多了。如果你對“二分”還沒有任何概念也沒關系,因為我們還有另外一種辦法也可以達到這個目的。
我們不妨設先後枚舉到的兩個相鄰的x分別為x1和x2,並設x1對應的max_y為max_y1,x2對應的max_y為max_y2,由於x1<x2,我們可以得到max_y2<=max_y1。但這個有什麽用呢?當然有用了,我們在找max_y2的時候就可以直接從max_y1開始遞減去找了。如果再枚舉到x3呢?那麽一樣有max_y3<=max_y2,我們直接從max_y2開始遞減去找max_y3。不過也許你可能會懷疑了:這樣真的能比兩層for循環快嗎?好像這樣也是兩個變量不停地在循環呀?
至於為什麽會快還是交由你自己來思考吧,如果你對剛剛說的思路還沒有什麽概念的話不妨結合下面我給出的示例代碼來看一下。

#include<stdio.h>
#define MAX 100001
int n[MAX], m[MAX],N,M,K;
int main()
{
    while(scanf("%d%d%d",&N,&M,&K)!=EOF)
    {
        for(int i=1;i<=N;i++)
        {
            scanf("%d",&n[i]); 
        } 
        for(int i=1;i<=M;i++)
        {
            scanf("%d",&m[i]); 
        } 
         
        
long long sum=0; for(int i=1;i<=N;i++) { while(n[i]+m[M]>K&&M) { --M; } sum+=M; } printf("%lld\n",sum); } return 0; }

csu oj 1343 Long Long