1. 程式人生 > >HDU 6058 列舉 Kanade's sum

HDU 6058 列舉 Kanade's sum

題目

Kanade's sum

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2496    Accepted Submission(s): 1037


Problem Description Give you an array A[1..n]  of length n

Let f(l,r,k) be the k-th largest element of A[l..r].

Specially , f(l,r,k)=0 if rl+1<
k
.

Give you k , you need to calculate nl=1nr=lf(l,r,k)

There are T test cases.

1T10

kmin(n,80)

A[1..n]isapermutationof[1..n]

n5105
Input There is only one integer T on first line.

For each test case,there are only two integers n,k on first line,and the second line consists of n integers which means the array A
[1..n]


Output For each test case,output an integer, which means the answer.
Sample Input 1 5 2 1 2 3 4 5
Sample Output

題目大意

   給我們一個數組A,給了我們一個區間,變成for迴圈就是for(l從1到n) for(r從l到n) 求區間內第K大的數,把這些第K大的數累計求和

解題思路

  採用列舉的思想,從每一個數組中的元素遍歷他的左右邊看分別有多少個比他大的數,這樣我們就可以知道該元素可以有多少個第K大的組合可能

#include<iostream>
#include<cstdio>
using namespace std;
#define read(a) scanf("%d",&a)
#define LL long long
const int maxn=500000+10;
int a[maxn];
int l[maxn],r[maxn];
int main()
{
       //freopen("1003.in", "r", stdin);
       //freopen("data.out", "w", stdout);
    int t;
    read(t);
    while(t--)
    {
        int n,k;
        read(n);
        read(k);
        for(int i=0;i<n;i++)
        {
            read(a[i]);
        }
        LL ans=0;
        for(int i=0;i<n;i++)
        {
            int lcnt=1,rcnt=1,j;//lcnt代表元素a[i]左邊比他大的數有多少個,rcnt同理
            for( j=i+1;j<n;j++)
            {
                if(rcnt>k)
                    break;
                if(a[j]>a[i])
                {
                    r[rcnt++]=j-i;//r[rcnt]代表右邊第rcnt個比a[i]大的數距離a[i]的距離,這個是方便計算的,可以等於j,
                                  //但是計算的時候要特殊處理右邊只有一個比a[i]大的時候,下方的rnum=1,比較麻煩,
                                  //原來是那樣做的,不建議
                }
            }
            if(j>=n)
                r[rcnt]=n-i; //如果a[i]右邊比他大的數沒超過k個,
                             //那麼我們知道a[i]右邊比他大的數只有rcnt-1個,
                             //我們假設距離a[i]最遠的比他大的那個數為righht,
                             //(程式中沒有right這個變數,這裡就是為了方便理解)
                             //這裡的r[rcnt]就是為了方便後面統計right右邊有多少個比a[i]小的數
            for(j=i-1;j>=0;j--)
            {
                if(lcnt>k)
                    break;
                if(a[j]>a[i])
                {
                    l[lcnt++]=i-j;//同理上面
                }
            }
            if(j<=0)
                l[lcnt]=i+1;//同理上面
            for(j=0;j<lcnt;j++)
            {
                if(k-j-1>=rcnt)
                    continue;
                int lnum=l[j+1]-l[j];
                int rnum=r[k-j]-r[k-j-1];
                ans+=(LL)a[i]*lnum*rnum;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


連結串列的程式碼,用連結串列省去了每次去尋找和比較比他大的數的時間,隨著N的增大節省時間越多

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define read(a) scanf("%d",&a)
const int maxn=5*1e5+10;
int pre[maxn],nex[maxn],pos[maxn],f[maxn],b[maxn],a[maxn],n,k;
void dele(int p)//更新前驅和後繼
{
    if(p==1)
        pre[nex[p]]=pre[p];
    else if(p==n)
        nex[pre[p]]=nex[p];
    else
    {
        pre[nex[p]]=pre[p];
        nex[pre[p]]=nex[p];
    }
    pre[p]=nex[p]=0;
}
void work()
{
  //  int n,k;
    memset(f,0,sizeof(f));
    memset(b,0,sizeof(b));

    read(n);read(k);
    for(int i=1;i<=n;i++)
    {
        read(a[i]);
        pos[a[i]]=i;//記錄a[i]的位置
        pre[i]=i-1;//記錄前驅
        nex[i]=i+1;//記錄後繼
    }
    LL ans=0;
    for(int i=1;i<=n-k+1;i++)//本程式碼的核心所在,因為是1-n的數所以可以是第k大的只可能是1-(n-k+1)這幾個數
                             //從1開始找,因為1最小所以它的兩邊都是比他大的數,找完1之後,在dele()函式裡面把1從連結串列裡面拿下來
                             //這樣繼續找2,因為1不在連結串列裡面了,所以2的兩邊都是比它大的數,以此類推直到n-k+1
    {
        int fro=0,beh=0;//front behind
        int p=pos[i];
        for(int v=p;v&&fro<=k;v=pre[v]) f[fro++]=v;
        for(int v=p;v<=n&&beh<=k;v=nex[v]) b[beh++]=v;
        f[fro]=0,b[beh]=n+1;
        for(int j=0;j<fro&&j<k;j++)//fro有可能是k+1,要有j<k限制他,MMP,找了半個多小時,煩躁
        {
            if(k-1-j>beh-1)
                continue;
           {
                int lnum=f[j]-f[j+1];
                int rnum=b[k-1-j+1]-b[k-1-j];
                ans+=1ll*i*lnum*rnum;
           }
        }
        dele(p);
    }
    printf("%lld\n",ans);
}
int main()
{
      //freopen("1003.in", "r", stdin);
      //freopen("data.out", "w", stdout);
    int T;
    read(T);
    while(T--)
    {
        work();
    }
    return 0;
}


相關推薦

HDU 6058 列舉 Kanade's sum

題目 Kanade's sum Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2496    Accept

2017 HDU 6058 多校聯合賽 Kanade's sum

Give you an array A[1..n] of length n . Let f(l,r,k) be the k-th largest element of A[l..r]. Specially , f(l,r,k)=0

HDU6058-Kanade's sum

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 664 Accepted Submiss

HDU6058 Kanade's sum

Kanade’s sum 題目大意就是給你一個1~n的一個排列,求滿足題意的所有區間[l,r](區間長度至少為k且1 <= l <= r <= n)中第k大的數之和。 舉個例子: 比如說給你 n = 4, k = 2,其中數列是a[4

hdu 6058 Kanade's sum(鏈表)

php blank pri 暴力 splay href .cn 每次 n) 題目鏈接:hdu 6058 Kanade‘s sum 題意: 給你一個n個數的排列,問你全部區間第k大的總和為多少。 題解: 我們只要求出對於一個數x左邊最近的k個比他大的和右邊最近k個比他大的,掃

hdu-6058 Kanade's sum

con sta cst getchar urn bsp pen define set   題意:略   思路:要我們求每個區間第K大數之和,其實可以轉換為求多少個區間的第K大數是X,然後我們在求和就好了。      那麽我們可以從小到大枚舉所有可能成為第K大的數。為什麽從小

【鏈表】2017多校訓練3 HDU 6058 Kanade's sum

iostream ++ 多校 open pos cnblogs names mat play acm.hdu.edu.cn/showproblem.php?pid=6058 【題意】 給定一個排列,計算 【思路】 計算排列A中每個數的貢獻,即對於每個ai,計算有

6058 Kanade's sum 鏈表維護()

bsp ans span min name -- const div amp 題意:給出元素為[1,n]的排列a,定義f[l,r,k]為區間[l,r]內第k大的元素.給出k,求 累加和(l=1~n,r~l~n)f[l,r,k] . n<=5e5,k<=min(8

HDU The Frog's Games(二分列舉

問題簡述: 給定一條河的寬度L、石頭的數量n、青蛙最多能跳躍的數量m以及每塊石頭距離岸邊的距離,求青蛙過河的最短步長。 問題分析: 只需關注當前的“利益”,跳一步跨過的石頭越多,用的總步數越少,自然滿足題意。 對於這種最值問題,如果滿足“單調性”,就可以直接用二分來做: &n

HDU 6059 Kanade's trio(字典樹)

Description 給出一個長度為n的序列A[1]~A[n],求滿足i < j < k且(A[i]^A[j])<(A[j]^A[k])的三元組(i,j,k)個數 Input 第一行一整數T表示用例組數,每組用例首先輸入一整數n表示序列

hdu 6058 連結串列 列舉

考慮每個數的貢獻,即滿足區間第k大為該數的區間個數。其實就是對於兩邊比起大的數,兩邊卡出比該數大的個數為k - 1. 用連結串列維護,考慮從小到大列舉,列舉完成後刪除該點,保證每次列舉連結串列中都是比

HDU 2852 KiKi's K-Number (樹狀數組 && 二分)

eof while ron name += oid names 如果 一個 題意:給出對容器的總操作次數n, 接下來是這n個操作。這裏對於一個容器提供三種操作, 分別是插入、刪除和查找。輸入0 e表示插入e、輸入1 e表示刪除e,若元素不存在輸出No Elment!、輸

hdu 3861 The King’s Problem trajan縮點+二分圖匹配

acm ant 二分 scanf title atan size pair city The King’s Problem Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768

HDU 4489 The King’s Ups and Downs

stdin algo pll class tar mem blank mes nbsp http://acm.hdu.edu.cn/showproblem.php?pid=4489 題意:有n個身高不同的人,計算高低或低高交錯排列的方法數。 思路:可以按照身高順序

HDU - 5172 GTY's gay friends

http cst con tin tdi tree net null void 題目鏈接 題意:n個數m個查詢,問[l,r]中的數是否為1到r-l+1的一個排列。 做法1:hash一下,對於[1...n],每個數都隨機分配一個hash值,一個集合的hash值為元素異或和

HDU 6043 KazaQ's Socks (規律)

n-1 cnblogs sample swe 順序 裏的 this c-s close Description KazaQ wears socks everyday. At the beginning, he has nn pairs of socks numbered

HDU 5172 GTY's gay friends (線段樹)

ace tac 分享 log 沒有 http inf hdu algorithm 題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=5172 題意:   給你一個n個數的數組,m次詢問,詢問在[L, R] 這個區間裏面有沒有

HDU - 2147 kiki's game

return to do hat won first appears perf art pear Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she ju

hdu 6058 To my boyfriend (計算貢獻,思維)

時間 () light 區間 frame enc const log -- 題意: 給你一個全排列,要你求這個序列的所有區間的第k大的和 思路:比賽的時候一看就知道肯定是算貢獻,也知道是枚舉每個數,然後看他在多少個區間是第K大,然後計算他的貢獻就可以了,但是沒有找到如何在o

hdu 6058 思維

return 例子 cstring sca 計算 ase 雙鏈表 += code 題目:http://acm.hdu.edu.cn/showproblem.php?pid=6058 分析題目的時候,由於枚舉的區間很多,而第k大的值範圍小,應該要想到去枚舉第k大的值然後找到這