[线段树][数学] Jzoj P4237 Melancholy

Description

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

Input

第一行给出两个整数,区域数N与计划数Q。
第二行给出N个整数,代表每一块区域的相对距离D。
第三行给出N个整数,代表每一块区域的内部地质元素的相对丰富程度V。
接下来的Q行,每一行3个整数,代表相对距离的限制L,R,以及选取的块数K。

Output

输出包括Q行,每一行一个整数,代表这项计划的评估值对2^32取模后的值。
 

Sample Input

5 3
5 4 7 2 6
1 4 5 3 2
6 7 1
2 6 2
1 8 3

Sample Output

5
52
924
 

Data Constraint

 

题解

  • 题目大意:有n个区间每个区间有一个相对距离D和矿值为V,每次询问相对距离在[l...r]中的选k个矿值的乘积之和(矿值最小的不选)
  • K=1
  • 显然,就是线段树区间查询就好了,就是V值之和-V值的最小值,只用维护区间和和区间最小值就好了
  • K=2
  • 貌似也不难,可以先计算没有减去最小V值的答案,那么只用记录一段区间和和最小值,然后先预处理出前缀和,然后根据这些其实就很容易推到答案
  • K=3、4、5、6
  • 本蒟蒻就不想推了,可以考虑也是线段树,维护一段区间取K个的乘积之和
  • 那么考虑一下对于两个区间怎么去合并,那么如果现在两个区间中一共要取k个
  • 那么是不是我们就要枚举两个区间内分别选多少个,然后可以将其乘起来p[i]=x[j]*y[i-j]/p[i]=x[j]*y[i-j-1]*y[7](代表的是当前的V值)
  • 这样的话我们就解决了区间合并的问题,那么剩下的就是线段树的维护就好了

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <cstring>
 5 #define N 100010
 6 using namespace std;
 7 int n,Q,l,r,k;
 8 unsigned int f[N*4][7],g[7],jc[7],p[7];
 9 struct edge{ int d,v; }a[N];
10 bool cmp(edge a,edge b) { return a.d<b.d; }
11 void work(unsigned int*x,unsigned int*y,unsigned int*k)
12 {
13     if (x[7]>y[7]) swap(x,y);
14     if (!x[7]) for (int i=0;i<=7;i++) k[i]=y[i];
15     else
16     {
17         memset(p,0,sizeof(p)),p[0]=1;
18         for (int i=1;i<=6;i++)
19         {
20             for (int j=0;j<=i;j++) p[i]+=x[j]*y[i-j];
21             for (int j=0;j<i;j++) p[i]+=x[j]*y[i-j-1]*y[7];
22         }
23         p[7]=x[7]; for (int i=0;i<=7;i++) k[i]=p[i];
24     }
25 } 
26 void build(int d,int l,int r)
27 {
28     f[d][0]=1;
29     if (l==r) { f[d][7]=a[l].v; return; }
30     int mid=l+r>>1;
31     build(d*2,l,mid),build(d*2+1,mid+1,r),work(f[d*2],f[d*2+1],f[d]);
32 }
33 void Query(int d,int l,int r,int L,int R)
34 {
35     if (a[l].d>R||a[r].d<L) return;
36     if (L<=a[l].d&&a[r].d<=R) { work(f[d],g,g); return; }
37     int mid=l+r>>1;
38     Query(d*2,l,mid,L,R),Query(d*2+1,mid+1,r,L,R);
39 }
40 int main()
41 {
42     scanf("%d%d",&n,&Q);
43     for (int i=1;i<=n;i++) scanf("%d",&a[i].d);
44     for (int i=1;i<=n;i++) scanf("%d",&a[i].v);
45     jc[0]=1; for (int i=1;i<=6;i++) jc[i]=jc[i-1]*i;
46     sort(a+1,a+n+1,cmp),build(1,1,n);
47     while (Q--) scanf("%d%d%d",&l,&r,&k),memset(g,0,sizeof(g)),g[0]=1,Query(1,1,n,l,r),printf("%lld\n",g[k]*jc[k]);
48 }

 

posted @ 2019-01-28 19:41  BEYang_Z  阅读(211)  评论(0编辑  收藏  举报