2022-04-22每日刷题打卡

2022-04-22每日刷题打卡

代码源——每日一题

古老的星球上有这样一群人,他们每年都会参加盛大的周年庆。在进入场地之前所有人在入口排成两队,每队人数都是 n 人,第一队第 i 人身高为 ai,第二队第 i 人身高为 bi。

人们在排队时,喜欢跟另一队相同位置的伙伴亲切地交谈。但是如果这两人身高相差太多,他们会感到有些尴尬,两人的尴尬指数 gi 与身高差呈这样一种关系:gi=(ai−bi)2。

为了尽可能减轻尴尬,每队的人都可以与前后相邻者交换位置,次数不限,但只能在同队范围内交换。问最少需要交换多少次位置,可以使得总体尴尬度 ∑(ai−bi)2 最小。如果答案太大,请输出这个最少交换次数对 108−7 取模的结果。

保证每队人的身高两两不同。

输入格式

输入共三行。第一行是一个正整数 n,表示每队的人数。 第二行有 n 个整数,每两个整数用一个空格隔开,表示第一队人们的身高。 第三行有 n 个整数,每两个整数用一个空格隔开,表示第二队人们的身高。

输出格式

一个整数,表示最少的交换次数。

数据范围

1≤n≤105,0≤ai,bi<231,保证每队人们的身高各不相同

输入样例

4
2 3 1 4
3 2 1 4

输出样例

1

样例解释

只需交换第一队前两人,或者交换第二队前两人。这样人们都跟与自己身高相同的伙伴交谈,总体尴尬度为0,即最小。

首先,我们要(a-b)2最小,我们把式子展开得到(a2-2*a *b-b2),然后我们应该清楚,不管a和b位置如何,最后的结果a2和b2这两种情况是改不了的,所以我们只能从2ab上下手,然后通过这个式子我们也能看出,要想(a-b)2最小,那么2ab应该要最大。

此时问题初步变成了,如何排序能让2ab最大。这里涉及到一个知识(我觉着挺神奇),就是说如果两个数组相乘,有序的数组相乘会大于无序的数组相乘。(关于证明感兴趣的可以去上网学习一下),所以我们现在要做的就是把一个数组对于另一个数组相对有序:两边数组第一大的在同一个位置,第三大的在同一个位置,第二大的在同一个位置(不是说数组必须有序,而是相对有序)。

现在题目最终变成了,每次可以交换两个相邻的数,问两个数组变的相对有序,步数最少是几步。这一步就相当于是求逆序对了,我们固定一个数组的顺序,仅改变另一个数组的值。那我们就根据固定的数组为准来离散化另一个数组,然后求出这个数组的逆序对数量,至于求逆序对用线段树或者逆序对都是可以的。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 1e5 + 50, MOD = 1e8 - 7;
ll f[4 * N];

void revise(int k, int l, int r, int x)
{
	if (l == r)
	{
		f[k] += 1;
		return;
	}
	int m = (l + r) / 2;
	if (x <= m)revise(k + k, l, m, x);
	else revise(k + k + 1, m + 1, r, x);
	f[k] = (f[k + k] + f[k + k + 1])%MOD;
}

ll calc(int k, int l, int r, int x, int y)
{
	if (l == x && y == r)
	{
		return f[k];
	}
	int m = (l +r) / 2;
	if (y <= m)return calc(k + k, l, m, x, y);
	else
		if (x > m)return calc(k + k + 1, m + 1, r, x, y);
		else return (calc(k + k, l, m, x, m) + calc(k + k + 1, m + 1, r, m + 1, y))%MOD;
}

int main() {
	int n;
	cin >> n;
	vector<PII>a(n), b(n);
	for (int i = 0; i < n; i++)
	{
		cin >> a[i].first;
		a[i].second = i;
	}
	for (int i = 0; i < n; i++)
	{
		cin >> b[i].first;
		b[i].second = i;
	}
	sort(a.begin(), a.end());
	sort(b.begin(), b.end());
	vector<int>v(n);
	for (int i = 0; i < n; i++)
	{
		v[a[i].second] = b[i].second + 1;
	}
	ll res = 0;
	for (int i = n-1; i >= 0; i--)
	{
		if (v[i] != 1)res = (res + calc(1, 1, n, 1, v[i] - 1))%MOD;
		revise(1, 1, n, v[i]);
	}
	cout << res << endl;
	
	return 0;
}

CodeForces——线段树专题

C - Segment Tree, part 2 - Addition and First element at least X

There is an array of n elements, initially filled with zeros. You need to write a data structure that processes two types of queries:

add v to all elements on the segment from l to r−1,
find the minimum index j such that j≥l and a[j]≥x.
Input
The first line contains two numbers n and m (1≤n,m≤100000), the size of the array and the number of operations. The following lines contain the description of the operations. The description of each operation is as follows:

1 l r v: add v to all elements on the segment from l to r−1 (0≤i<n, 0≤v≤10^4).
2 x l: find the minimum index j such that j≥l and a[j]≥x (0≤x≤10^9, 0≤l<n). If there is no such element, print −1.
Indices start from 0.
Output
For each operation of the second type, print the answer for the query.

Example

input

5 7
1 2 5 3
1 1 3 4
2 3 0
2 5 0
1 4 5 5
2 5 3
2 8 1

output

1
2
4
4

题目是说给你一个初始为0的数组,进行两种操作,一个是把区间l~r的数都加上v,一种是让你找到下标大于等于j,且值大于等于x的第一个节点的位置。

区间加就是很基础的标记下传,这里主要讲第二种操作,线段树节点记录的是区间内的最大值,每次我们判断一下,如果左子节点的值大于x,那我们优先去左边找,如果找到的节点下标小于j,那我们就返回一个-1,然后回到上一层去右边找,也就是说我们这个线段树要有个回溯功能,如果再去右边找,找到的节点是小于x或是下标小于j的,那也返回-1。

但是这种做法,最坏情况下我们一次查询可能要把整个线段树跑一边,相当慢了,所以我们要减枝。当当前区间的最大值是小于x时,我们也不用纠结去左或右了,直接返回-1即可。或者当前区间的下标都是小于j的,我们也可直接返回-1。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#define endl '\n';
typedef long long ll;
typedef pair<ll, ll>PII;
const int N = 100500;
ll f[4 * N], b[4 * N], lmax[4 * N], rmax[4 * N], date[4 * N];

void push_down(ll k, ll l, ll r)
{
    if (b[k] != 0)
    {
        int m = (l + r) / 2;
        b[k + k] += b[k];
        b[k + k + 1] += b[k];
        f[k + k] += b[k];
        f[k + k + 1] += b[k];
        b[k] = 0;
    }
}

void revise(ll k, ll l, ll r, ll x, ll y, ll v)
{
    if (l == x && r == y)
    {
        b[k] += v;
        f[k] += v;
        return;
    }
    push_down(k, l, r);
    
    int m = (l + r) / 2;
    if (y <= m)revise(k + k, l, m, x, y, v);
    else
        if (x > m)revise(k + k + 1, m + 1, r, x, y, v);
        else
        {
            revise(k + k, l, m, x, m, v);
            revise(k + k + 1, m + 1, r, m + 1, y, v);
        }
    f[k] = max(f[k + k], f[k + k + 1]);
}

ll calc(int k, int l, int r, int x, int y)
{
    if (l == r)
    {
        if (l < y || f[k] < x)return -1;
        return l;
    }
    push_down(k, l, r);
    int m = (l + r) / 2;
    if (f[k] < x || r < y)return -1;
    int res = -1;
    if (f[k + k] >= x)res = calc(k + k, l, m, x, y);
    if (res == -1)res = calc(k + k + 1, m + 1, r, x, y);
    f[k] = max(f[k + k], f[k + k + 1]);
    return res;
}

int main()
{
    int n, m;
    cin >> n >> m;
    while (m--)
    {
        ll t, x, y, v;
        cin >> t >> x >> y;
        if (t == 1)
        {
            cin >> v;
            revise(1, 1, n, x + 1, y, v);
        }
        else cout << max((ll)-1, calc(1, 1, n, x, y + 1) - 1) << endl;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值