传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6464
免费送气球
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 264 Accepted Submission(s): 53
Problem Description
又到了GDUT一年一度的程序设计竞赛校赛的时间啦。同学们只要参加校赛,并且每解出一道题目就可以免费获得由ACM协会和集训队送出的气球一个。听到这个消息,JMC也想参加免费拿气球。可是,由于JMC太菜了而被禁止参赛,于是他找到你想让你帮忙参加比赛,可以通过执行下面的C++程序解决问题后获得气球并送给他。JMC保证了下面的程序一定能获得正确的结果。
void solve(int Q, int type[], long long first[], long long second[]) {
vector<long long> vec;
for (int i = 0; i < Q; ++i) {
if (type[i] == 1) {
long long k = first[i], val = second[i];
while (k--) {
vec.push_back(val);
}
}
else if (type[i] == 2) {
sort(vec.begin(), vec.end());
long long l = first[i] - 1, r = second[i], res = 0;
while (l < r) {
res = (res + vec[l++]) % 1000000007;
}
printf("%lld ", res);
}
}
}
为防止你被JMC的代码搞到头晕目眩,JMC特意给出了问题的文字描述。已知一开始有一个空序列,接下来有Q次操作,每次操作给出type、first和second三个值。当type为1时,意味着该操作属于第一种操作:往序列尾部添加first个second数。当type为2时,意味着该操作属于第二种操作:查询序列中第first小至第second小的数值之和(一共有(second - first + 1)个数被累加),并将结果对1000000007取模后输出。
void solve(int Q, int type[], long long first[], long long second[]) {
vector<long long> vec;
for (int i = 0; i < Q; ++i) {
if (type[i] == 1) {
long long k = first[i], val = second[i];
while (k--) {
vec.push_back(val);
}
}
else if (type[i] == 2) {
sort(vec.begin(), vec.end());
long long l = first[i] - 1, r = second[i], res = 0;
while (l < r) {
res = (res + vec[l++]) % 1000000007;
}
printf("%lld ", res);
}
}
}
为防止你被JMC的代码搞到头晕目眩,JMC特意给出了问题的文字描述。已知一开始有一个空序列,接下来有Q次操作,每次操作给出type、first和second三个值。当type为1时,意味着该操作属于第一种操作:往序列尾部添加first个second数。当type为2时,意味着该操作属于第二种操作:查询序列中第first小至第second小的数值之和(一共有(second - first + 1)个数被累加),并将结果对1000000007取模后输出。
Input
单组数据
第一行一个Q(1 <= Q <= 1e5),代表Q次操作。
接下来有Q行,每行包含三个整数type、first和second;其中1 <= type <= 2。当type等于1时,0 <= first,second < 1e9。当type等于2时,1 <= first <= second,且first和second均不大于目前已添加进序列的数的数量。
第一行一个Q(1 <= Q <= 1e5),代表Q次操作。
接下来有Q行,每行包含三个整数type、first和second;其中1 <= type <= 2。当type等于1时,0 <= first,second < 1e9。当type等于2时,1 <= first <= second,且first和second均不大于目前已添加进序列的数的数量。
Output
对于每次操作二,将结果对1000000007取模后输出。
Sample Input
6
1 5 1
1 6 3
2 2 5
2 4 8
1 2 2
2 4 8
Sample Output
4
11
9
Source
解题思路:
理解学习一波主席树:https://blog.csdn.net/g21wcr/article/details/82970228
1操作正常建权值线段树,查询操作则是利用 前缀和作差的方法来求区间和。
AC code:
1 #include <bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 const LL INF = 0x3f3f3f3f; 5 const LL MOD = 1e9+7; 6 const int MAXN = 1e5+10; 7 8 LL sum[MAXN*40], val[MAXN*40]; 9 int ls[MAXN*40], rs[MAXN*40]; 10 int cnt; 11 12 void update(int &Root, int L, int R, LL k, LL num) 13 { 14 if(!Root) Root = ++cnt; 15 sum[Root]+=k; 16 val[Root]+=(LL)k*num; 17 val[Root]%=MOD; 18 if(L == R) return; 19 int mid = (L+R)>>1; 20 if(num <= mid) update(ls[Root], L, mid, k, num); 21 else update(rs[Root], mid+1, R, k, num); 22 } 23 24 LL query(int Root, int L, int R, LL k) //前 k 小的值的和 25 { 26 if(L == R) return 1LL*k*L%MOD; 27 int mid = (L+R)>>1; 28 LL ans = 0; 29 if(k > sum[ls[Root]]) 30 ans = (val[ls[Root]] + query(rs[Root], mid+1, R, k-sum[ls[Root]]))%MOD; 31 else 32 ans = query(ls[Root], L, mid, k); 33 34 return ans%MOD; 35 } 36 37 int main() 38 { 39 int Q_case, rt = 0; 40 scanf("%d", &Q_case); 41 while(Q_case--){ 42 int op; 43 LL L, R; 44 scanf("%d %lld %lld", &op, &L, &R); 45 if(op == 1) update(rt, 0, 1e9, L, R); 46 else{ 47 LL ans = (query(rt, 0, 1e9, R) - query(rt, 0, 1e9, L-1)%MOD + MOD)%MOD; //类似于利用前缀和的差 来 求解区间的和 48 printf("%lld ", ans); 49 } 50 } 51 return 0; 52 }