优化还是新算法来解决这个问题?

时间:2013-06-18 13:56:30

标签: c++ algorithm data-structures segment-tree

我正在尝试解决这个问题:

小女孩有一个n个元素的数组(数组的元素从1开始索引)。

此外,还有“q”个查询,每个查询由一对整数 li,ri(1≤li≤ri≤n)定义。您需要为每个查询找到具有从li到ri(包括)的索引的数组元素的总和。

小女孩发现这个问题很无聊。她决定在回复查询之前重新排序数组元素,使查询回复的总和最大化。你的任务是找到这个最大金额的值。


输入:

第一行包含两个空格分隔的整数n(1≤n≤10^ 5)和q(1≤q≤10^ 5) - 数组中的元素数和相应的查询数。 / p>

下一行包含n个以空格分隔的整数ai(1≤ai≤10^ 5) - 数组元素。

以下q行中的每一行都包含两个以空格分隔的整数 li和ri(1≤li≤ri≤n) - 第i个查询。


输出:

在一行中打印一个整数 - 重新排序数组元素后查询答复的最大总和。

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

我了解Segment树,所以我通过分段树应用了Lazy传播方法。

我的努力代码:

#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;   
}

我的处理方法很简单就像使用段树一样,最后打印出答案。

我的问题是,有没有更简单的Algo?或者我应该优化我的分段树代码?

3 个答案:

答案 0 :(得分:1)

我认为这会奏效 - 邀请评论

创建一个名为count的维数n数组,并将其初始化为0 通过Q数组

对于每个查询    - 从li到ri增加计数1,即元素li到ri的计数 对数组进行排序n 对count数组进行排序(记住索引) 拿起最高的计数并在相应的索引处放置N中的最高元素 继续这个所有元素

基本上我们确保最高元素出现的次数最多(当被查询引用时)

答案 1 :(得分:1)

这就是我要做的事情:

  1. 创建(哈希)地图索引 - &gt;计数。遍历所有查询以及范围中的每个索引,递增计数(*)。
  2. 按大小排序数组中的元素,降序(现在称为values)。
  3. 从你的hashmap中提取counts(索引现在不相关,因为我们不再需要它们,并且数组也会相应地重新排序),并且还要命令它们降序。
  4. 遍历有序的计数数组并总结sum += counts[i] * values[i]
  5. 假设您的数组是

    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
    

    一个完美的重新排序(与算法无关,因为只需要总和)

    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的数组/向量而不是映射,并使用索引作为键。如果只是在我的例子中提到了地图,因为它使这个想法更加明显

答案 2 :(得分:1)

<强>概念:  “你必须将数组中最大的元素修复到大多数时候被查询的索引,然后是第二大查询元素的第二大元素

这是我的方法的实现:

#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. 只是增加每个起始位置的计数和一个多于结束位置的计数,因为你必须包括直到结束。
  3. 最后计算所有计数。在O(n)。你可以知道每次增加的时间。对它进行排序并对给定的数组进行排序,并将数字乘以如此获得的频率,然后让你回答。

  4. 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