0

私はこの問題を解決しようとしています:

少女には n 個の要素の配列があります (配列の要素には 1 から始まるインデックスが付けられます)。

また、"q" 個のクエリがあり、それぞれが一対の整数li, ri (1 ≤ li ≤ ri ≤ n)によって定義されます。クエリごとに、li から ri までのインデックスを持つ配列の要素の合計を見つける必要があります。

少女はその問題がかなりつまらないことに気づきました。彼女は、クエリ応答の合計が可能な限り最大になるように、クエリに応答する前に配列要素を並べ替えることにしました。あなたの仕事は、この最大合計の値を見つけることです。


入力:

最初の行には、スペースで区切られた 2 つの整数 n (1 ≤ n ≤ 10^5) と q (1 ≤ q ≤ 10^5) が含まれています。これは、配列内の要素の数とクエリの数です。

次の行には、スペースで区切られた n 個の整数 ai (1 ≤ ai ≤ 10^5) — 配列要素が含まれます。

次の q 行のそれぞれには、スペースで区切られた 2 つの整数li と ri (1 ≤ li ≤ ri ≤ n) — i 番目のクエリが含まれます。


出力:

1 行に 1 つの整数 (配列要素が並べ替えられた後のクエリ応答の最大合計) を出力します。

Sample testcases:

input:
3 3
5 3 2
1 2
2 3
1 3
output
25

input
5 3
5 2 4 1 3
1 5
2 3
2 3
output
33

私はセグメント ツリーの知識を持っているので、セグメント ツリーを介して遅延伝播アプローチを適用しました。

私の努力コード:

#include <iostream>
#include <string>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#include <vector>
#include <set>
#include <list>
#include <cmath>
#include <stack>
using namespace std;

#define scan(x) scanf("%d",&x)
#define print(x) printf("%d ",x)
#define println(x) printf("%d\n",x)
#define For(i,a,j,k) for (i = a; i < j; i+= k)
#define For_back(i,a,j,k) for (i = j; i >= a; i-= k)
#define SET(a,x) memset(a,x,sizeof(a))
#define mod 1000000007
#define inf 0x7fffffff
#define Max 2000000

typedef pair<int,int> pii;
typedef pair<pii,int> piii;

long long int tree[3*Max];
long long int lazy[3*Max];

void update(int node,int a,int b,int i,int j,long long int value)
{
    if (lazy[node]!= 0)
    {
        tree[node] += lazy[node];

        if (a != b)
        {
            lazy[2*node] += lazy[node];
            lazy[2*node+1] += lazy[node];
        }
        lazy[node] = 0;
    }
    if (a > b || a > j || b < i)
        return;
    if (a >= i && b <= j)
    {
        tree[node] += value;
        if (a != b)
        {
            lazy[2*node] += value;
            lazy[2*node+1] += value;
        }
        return;
    }
    int mid = (a+b)/2;
    update(2*node,a,mid,i,j,value);
    update(2*node+1,mid+1,b,i,j,value);

    tree[node] = (tree[2*node]+tree[2*node+1]);
}

long long int query(int node,int a,int b,int i,int j)
{
    if (a> b || a > j || b < i) return 0;

    if (lazy[node] != 0)
    {
        tree[node] += lazy[node];
        if (a != b)
        {
            lazy[2*node] += lazy[node];
            lazy[2*node+1] += lazy[node];
        }
        lazy[node] = 0;
    }
    if (a >= i && b <= j)
        return tree[node];
    int mid = (a+b)/2;
    long long int q1 = query(2*node,a,mid,i,j);
    long long int q2 = query(2*node+1,mid+1,b,i,j);

    return ((q1+q2));
}
int main()
{
    SET(lazy,0);
    SET(tree,0);

    int n,m;
    cin >> n >> m;
    int i,j;
    int arr[n];
    For(i,0,n,1)
    {
        cin >> arr[i];
    }
    sort(arr,arr+n);
    For(i,0,m,1)
    {
        long long int num1,num2;
        cin >> num1 >> num2;

        update(1,0,n-1,num1-1,num2-1,1);
    }
    long long int my[n];
    For(i,0,n,1)
    {   
        long long int number = query(1,0,n-1,i,i);       
        my[i] = number;
    }
    sort(my,my+n);
    long long int sum = 0;
    For_back(i,0,n-1,1){
        sum += my[i]*arr[i];
    }
    cout << sum << endl;
    return 0;   
}

これに対する私のアプローチは、セグメントツリーを使用して前述のように実行し、最後に答えを出力するだけの簡単なものでした。

私の質問は、これのためのより単純なアルゴリズムはありますか? または、セグメント ツリー コードを最適化する必要がありますか?

4

3 に答える 3

1

これでうまくいくと思います - コメント募集中

count という n 次元の配列を作成し、それを 0 に初期化します Q 配列を調べます

クエリごとに - li から ri まで count を 1 ずつインクリメントします。つまり、要素の数 li から ri を並べ替えます。 n 配列を並べ替えます。 count 配列を並べ替えます (インデックスを覚えておいてください)。 N すべての要素についてこれを続けます

基本的に、最高の要素が最高の回数発生するようにしています (クエリによって参照された場合)。

于 2013-06-18T14:19:50.407 に答える
1

概念: 「配列の最大の要素を最も多くクエリされるインデックスに修正し、次に 2 番目に大きな要素を 2 番目にクエリされる要素に修正する必要があります。

これが私のメソッドの実装です:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
using namespace std;
int main()
{
    LL n,q,l,r,i;
    cin>>n>>q;
    LL arr[n];
    for(i=0;i<n;i++)
        cin>>arr[i];
    LL freq[n];
    memset(freq,0,sizeof freq);
    sort(arr,arr+n);
    for(i=0;i<q;i++)
    {
        cin>>l>>r;
        freq[l-1]++; // DP method of freq
        if(r<n)     
        freq[r]--;
    }
    for(i=1;i<n;i++)
        freq[i]+=freq[i-1];
    sort(freq,freq+n);
    LL ans=0;
    for(i=n-1;i>=0;i--)
        if(freq[i])
            ans+=arr[i]*freq[i];
    cout<<ans<<endl;
    return 0;
}

ええ、配列を並べ替えてから頻度を並べ替えてから、数値に頻度を掛ける必要があります。これにより、最大合計が得られます..

カウントを維持する方法:

  1. li から ri に毎回更新しないでください。
  2. 代わりに、各開始位置と終了位置よりも 1 つ多い位置でカウントを増やします。これは、最後まで含める必要があるためです。
  3. 最後にすべてのカウントを合計します。O(n)で。そして、それぞれが何回増加したかを知ることができます。それを並べ替えて、指定された配列を並べ替え、得られた頻度で数値を掛けて、答えてもらいます。

input: 5 3
array : 5 2 4 1 3
1st query: 1 5
freq update = 1 0 0 0 0 
2nd query: 2 3
freq update =1 1 0 -1 0 
3rd query: 2 3
freq update= 1 2 0 -2 0 
collective freq=1 3 3 1 1 
sorted freq= 1 1 1 3 3 
sorted array =1 2 3 4 5 
ans =33
于 2013-06-18T14:44:35.397 に答える
1

これは私がすることです:

  1. (ハッシュ) マップ index->​​count を作成しました。すべてのクエリを実行し、範囲内のインデックスごとにカウントを増やします (*)。
  2. 配列内の要素をサイズ順に並べ替えます (現在呼び出されvaluesています)。
  3. ハッシュマップから を抽出しcounts(インデックスは不要になり、配列はそれに応じて並べ替えられるため、現在は無関係です)、降順で並べ替えます。
  4. 順序付けられたカウントの配列を反復処理し、合計しますsum += counts[i] * values[i]

あなたの配列が

0, 1, 2, 3, 4, 5, 6, 7, 8, 9

クエリは次のとおりです。

q1: 1-3
q2: 2-4
q3: 3-5

地図:

1->1
2->2
3->3
4->2
5->1

並べ替えられたカウント:

3,2,2,1

完全な並べ替えの 1 つ (合計のみが必要なため、アルゴリズムには関係ありません)

6,7,9,8,5,4,3,2,1,0

クエリの合計:

(6 + 7 + 9) + (7 + 9 + 8) + (9 + 8 + 5) = 68

アルゴリズムで:

3 * 9 + 2 * 8 + 2 * 7 + 1 * 6 + 1 * 5 = 68

(*) これを高速化したい場合は、マップの代わりにサイズ n の配列/ベクトルを使用し、インデックスをキーとして使用できます。アイデアをより明確にするために、私の例でマップについて言及した場合

于 2013-06-18T14:32:05.213 に答える