L R数组区间加1处理(可能叫差分)

sort函数+l r输入处理方法。
链接:https://ac.nowcoder.com/acm/contest/5477/F
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
天才程序员菜哭武和石头组队参加一个叫做国际排列计算竞赛 (International Competition of Permutation Calculation, ICPC) 的比赛,这个比赛的规则是这样的:

一个选手给出一个长度为 n 的排列,另一个选手给出 m 个询问,每次询问是一个形如 (l, r) 的数对,查询队友给出的排列中第 l 个数到第 r 个数的和,并将查询到的这个区间和加入总分,最后总分最高的队伍就能获胜。

石头手速很快,在比赛一开始就给出了 m 个询问;菜哭武也很强,他总是能找到最合适的排列,使得他们队的总分尽可能高。

在看比赛直播的你看到了石头给出的 m 个询问,聪明的你能不能预测出他们队伍最终的得分呢?

一个排列是一个长度为 n 的数列,其中 1 ~ n 中的每个数都在数列中恰好出现一次。比如 [1, 3, 2] 是一个排列,而 [2, 1, 4] 和 [1, 2, 3, 3] 不是排列。

输入描述:
第一行输入两个数 n (1≤n≤2×105) 和 m (1≤m≤2×105) 。
接下来 m 行,每行输入两个数 l 和 r ,代表这次查询排列中第 l 个到第 r 个的和。

输出描述:
输出一个整数,代表他们队伍总分的最大值。
示例1
输入
复制
7 3
1 3
3 7
5 6
输出
复制
46
说明
一个符合条件的排列是 [1,3, 6, 4, 7, 5, 2],于是最终的得分为 (1 + 3 + 6) + (6 + 4 + 7 + 5 + 2) + (7 + 5) = 46
有1到n个数字,然后m个查询,构造一个序列,使得查询后的值最大化。
因为只输出一次,不需要关心到底是怎么构造的,考虑,被查询到的数字次数越多,那么就让他的值越大,则,差分前缀和求出每个数字被查询的次数,然后排序,出现次数最小的对应1,最大的对应n即可
#include
#include
#include
#define ll long long
using namespace std;
const int N = 2e5 + 5;
int d[N];
int main(){
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i++){
int l, r;
scanf("%d%d", &l, &r);
d[l] ++ ,d[r + 1]–;
}
for(int i = 1; i <= n; i++){
d[i] += d[i - 1];
}
sort(d + 1, d + n + 1);
ll ans = 0;
for(int i = 1; i <= n; i++)
ans += (ll) d[i] * i;
printf("%lld\n", ans);
return 0;
}

这个题的数组如果定义为int需要再加的时候longlong转换一下。不然只会过一部分数据。无奈。咱也说不上来为啥,直接数组搞成longlongint就好啦。没那摩多事。

c++;
刚拿到题目的时候看到排列,想用next_permutation()全排列一个个比较结果,算出最大的结果,于是暴试,发现数据太大实在不可能。

最后的输出只要求结果,不要求中间的具体排列组合。
然后一直在从样例里找灵感,发现样例的结果其实就是1~7(n)中间的数字加起来,并且最后加上重复的两个不同的数字,而题目又要求结果最大,故使得最后两个不同的数字在n中是最大的。所以用一个数组存储每次l、r及其中间数字的出现次数,并以1加到n为参考,找出缺漏的数字和重复的数字还有他们的个数,在1加到n的结果上加减。然后发现大的思路没什么问题,但是这个实现实在太麻烦了,也会超时。

思路详解:
首先我们要确立每个数字被查询的次数。这个时候可以用差分前缀和来记录每个数字的次数。然后排序,记录次数最小的对应1,记录次数最大的对应n即可,中间次数即对应由1至n递增的数字,并且把对应的数字乘记录次数的结果累加就是最后的结果
#include
#define fio ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long int ll;
using namespace std;

ll d[200005];
int main() {
int n, m;
ll sum = 0;
cin >> n >> m;
memset(d, 0, sizeof(d));
int x, y;
while (m–) {
cin >> x >> y;
d[x]++;d[y+1]–;
}
for (int i = 1; i <= n; i++) {
d[i] = d[i] + d[i-1];//前缀和
}
sort(d+1, d+n+1);
for (int i = 1; i <= n; i++) {
sum += d[i] * i;//对应排列数字记录次数
}
cout << sum << endl;
}
java
考点:贪心+差分
首先我们要考虑计算每个位置的访问次数。
我们可以用差分来计算每一个位置访问了多少次。
然后排序,将从小到大以此乘以1 2 3 4~~~,求和即可。
import java.util.
;
import java.math.;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.PrintWriter;
public class Main {
public static void main(String args[])throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
in.nextToken();
int n = (int)in.nval;
in.nextToken();
int m = (int)in.nval;
long num[] = new long[n];
long p[] = new long[n+1];
for(int t=0;t {
in.nextToken();
int l = (int)in.nval;
in.nextToken();
int r = (int)in.nval;
p[l-1]+=1;
p[r]-=1;
}
num[0] = p[0];
long u=p[0];
for(int i=1;i {
num[i] = p[i]+u;
u+=p[i];
}
long sum=0;
Arrays.sort(num);
for(int i=0;i {
sum+=(num[i]
(i+1));
}
out.print(sum);
out.flush();

}

}

你可能感兴趣的:(笔记)