4237. 【五校联考5day1】Melancholy (Standard IO)

题面:

DX3906星系,Melancholy星上,我在勘测这里的地质情况。
我把这些天来已探测到的区域分为N组,并用二元组(D,V)对每一组进行标记:其中D为区域的相对距离,V为内部地质元素的相对丰富程度。
在我的日程安排表上有Q项指派的计划。每项计划的形式是类似的,都是“对相对距离D在[L,R]之间的区域进行进一步的勘测,并在其中有次序地挑出K块区域的样本进行研究。”采集这K块的样品后,接下来在实验中,它们的研究价值即为这K块区域地质相对丰富程度V的乘积。
我对这Q项计划都进行了评估:一项计划的评估值P为所有可能选取情况的研究价值之和。
但是由于仪器的原因,在一次勘测中,这其中V最小的区域永远不会被选取。
现在我只想知道这Q项计划的评估值对2^32取模后的值,特殊地,如果没有K块区域可供选择,评估值为0。

样例

5 3

5 4 7 2 6

1 4 5 3 2

6 7 1

2 6 2

1 8 3

数据约束

5c4f099a25a9f.png

题目大意

给你一个序列,每次询问一个区间 \(l...r\) , 问从中选出K个数(除其中最小值)的所有组合的价值和(所选数的乘积)。

考场思路

先对D值进行排序,这样每次实质询问区间 $ L,R $ 都可以通过二分来确定;
在考虑如何统计答案。

先不考虑不能选区间中最小值的限制,考虑不算或者减去。
观察到K的值较小,我们联想到几种大体思路:

1.合并两区间答案。假设我已经求出了区间\(l , m\) 和 $ m + 1 , r$ 的 较小的K的答案,显然可合并答案求出大K的答案。

2.容斥(此处不详讲)。

考虑到区间查询以及 \(n\) 的 规模,我们采用线段树。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define R register 

using namespace std;

const int N = 1e5 + 10;
inline void read(int &x)
{
    x = 0;char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = x * 10 + c - '0' , c = getchar();
}

int n , q , tmp[N];
unsigned fact[10]; 

struct SGT
{
    unsigned val[6] , mi , po;
    void clear(){memset(val , 0 , sizeof val) , mi = 0x3f3f3f3f , po = 0;}
}g[N * 25];

SGT merge(SGT a ,SGT b)
{
    SGT ret;ret.clear();
    (a.mi < b.mi) && (ret.mi = a.mi , ret.po = a.po , 1) || (ret.mi = b.mi , ret.po = b.po);
        
    for(R int i = 1;i <= 6;i ++)
    {
        ret.val[i - 1] = a.val[i - 1] + b.val[i - 1];
        for(R int j = 1;j < i;j ++)
            ret.val[i - 1] += a.val[j - 1] * b.val[i - j - 1];  
    }
    return ret;     
}

struct note
{
    int D , V;
}rec[N];
int cmp(note a, note b){return a.D < b.D;}

void pre()
{
    for(R int i = fact[0] = 1;i <= n;fact[i] = fact[i - 1] *  i ,i ++);
}

void build(int l , int r ,int k)
{
    if(l == r) return(void)(g[k].val[0] = g[k].mi = rec[l].V , g[k].po = l);
    int mid = l + r >> 1;
    build(l , mid , k << 1) , build(mid + 1 , r , k << 1 | 1) , g[k] = merge(g[k << 1] , g[k << 1 | 1]);
}

void qry(int l , int r,int k,int _l,int _r,int tar ,SGT &x)
{
    if(l >= _l && r <= _r) return (void) (x = merge(x , g[k]));
    int mid = l + r >> 1;
    if(_l <= mid) qry(l , mid , k << 1 , _l , _r , tar , x);
    if(_r > mid) qry(mid + 1 , r, k << 1 | 1, _l , _r , tar , x);
}

unsigned done(int l, int r , int K)
{
    l = lower_bound(tmp + 1 , tmp + 1 + n , l) - tmp, r = upper_bound(tmp + 1 , tmp + 1 + n , r) - tmp - 1;
    if(r - l < K) return 0;
    SGT t , ret1 , ret2;ret1.clear() , ret2.clear() , t.clear();
    
    qry(1 , n , 1 , l , r , K , t);
    if(l < t.po) qry(1 , n , 1 , l , t.po - 1 , K, ret1);
    if(r > t.po) qry(1 , n , 1 , t.po + 1 , r , K , ret2);
        
    ret1 = merge(ret1 , ret2);
    return ret1.val[K - 1] * fact[K];
}

int main()
{
    read(n) , read(q);
    for(R int i = 1;i <= n;i ++) read(rec[i].D);
    for(R int i = 1;i <= n;i ++) read(rec[i].V);
    sort(rec + 1 , rec + 1 + n ,cmp) , pre();
    for(R int i = 1;i <= n;i ++) tmp[i] = rec[i].D;

    build(1 , n , 1);

    for(int l , r , K; q ; q --)
        read(l ) , read(r) , read(K),
        printf("%u\n",done(l , r , K));
    return 0;
}

别忘记隔绝最小值所参与的运算。
(因为取模数比较特别,可以利用 \(unsigned int\) 的自然溢出,减小高频取模的问题。)

转载于:https://www.cnblogs.com/Sin-demon/p/10332018.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值