Codeforces 371E Subway Innovation【思维+前缀和】

E. Subway Innovation
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Berland is going through tough times — the dirt price has dropped and that is a blow to the country's economy. Everybody knows that Berland is the top world dirt exporter!

The President of Berland was forced to leave only k of the currently existing n subway stations.

The subway stations are located on a straight line one after another, the trains consecutively visit the stations as they move. You can assume that the stations are on the Ox axis, the i-th station is at point with coordinate xi. In such case the distance between stations i and j is calculated by a simple formula |xi - xj|.

Currently, the Ministry of Transport is choosing which stations to close and which ones to leave. Obviously, the residents of the capital won't be too enthusiastic about the innovation, so it was decided to show the best side to the people. The Ministry of Transport wants to choose such k stations that minimize the average commute time in the subway!

Assuming that the train speed is constant (it is a fixed value), the average commute time in the subway is calculated as the sum of pairwise distances between stations, divided by the number of pairs (that is ) and divided by the speed of the train.

Help the Minister of Transport to solve this difficult problem. Write a program that, given the location of the stations selects such k stations that the average commute time in the subway is minimized.

Input

The first line of the input contains integer n (3 ≤ n ≤ 3·105) — the number of the stations before the innovation. The second line contains the coordinates of the stations x1, x2, ..., xn ( - 108 ≤ xi ≤ 108). The third line contains integer k (2 ≤ k ≤ n - 1) — the number of stations after the innovation.

The station coordinates are distinct and not necessarily sorted.

Output

Print a sequence of k distinct integers t1, t2, ..., tk (1 ≤ tj ≤ n) — the numbers of the stations that should be left after the innovation in arbitrary order. Assume that the stations are numbered 1 through n in the order they are given in the input. The number of stations you print must have the minimum possible average commute time among all possible ways to choose k stations. If there are multiple such ways, you are allowed to print any of them.

Examples
Input
3
1 100 101
2
Output
2 3 
Note

In the sample testcase the optimal answer is to destroy the first station (with x = 1). The average commute time will be equal to 1 in this way.


题目大意:


给你N个坐标,然后让你选出k个点,使得这k个点中任意取出两点的值:|Ai-Aj|的和最小。

问这个几个坐标的编号。


思路:


不难想到,我们要选的这K个点一定是连续的K个点。

那么我们只要考虑怎样能够在时间复杂度内统计每个点作为起点的价值总和即可。


1、首先我们将所有坐标从小到大排序。

然后我们可以O(k)求出第一段长度为K的子序列的总价值和:


①我们要维护一个数组sum【i】,表示前i个坐标的坐标位子和。

②那么第一段长度为K的子序列的总价值和为:

(i-1)*a【i】-sum【i-1】(2<=i<=k)


2、如果我们暴力枚举起点然后O(k)去求区间总价值和的话,时间复杂度是O(nk)的,显然会超时。

那么我们考虑,对于第一段长度为K的子序列的和的总价值,和第二段长度为k的子序列的和的总价值之间有什么关系?



很显然,蓝色的线是减少的,橙黄色的线是增加的。

这里一条线的意义表示为右边的那个点减去左边那个点的值。


那么我们有前缀和,那么O(1)能够更新下一个区间的总价值和。

细节参考代码


整体思路就是这样,细节参考代码不是很难。

注意数据范围。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define ll __int64
struct node
{
    ll val,pos;
}a[350000];
ll sum[350000];
int cmp(node a,node b)
{
    return a.val<b.val;
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)scanf("%I64d",&a[i].val),a[i].pos=i;
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;i++)
        {
            if(i==1)sum[i]=a[i].val;
            else
            sum[i]=sum[i-1]+a[i].val;
        }
        ll maxn=-1000000000000000000;
        ll ans=-1;
        ll k;
        scanf("%I64d",&k);
        ll pre=0;
        for(int i=2;i<=k;i++)
        {
            pre+=(i-1)*a[i].val-sum[i-1];
        }
        maxn=max(maxn,pre);
        ans=1;
        for(ll i=3;i<=n;i++)
        {
            if(i-1+k-1<=n)
            {
                pre+=(k-1)*a[i-2].val-(sum[i-2+k-1]-sum[i-2]);
                pre+=(k-1)*a[i-1+k-1].val-(sum[i-1+k-1-1]-sum[i-2]);
                if(pre<maxn)
                {
                    maxn=pre;
                    ans=i-1;
                }
            }
            else break;
        }
        for(ll i=1;i<=k;i++)
        {
            printf("%I64d ",a[ans+i-1].pos);
        }
        printf("\n");
    }
}












### 关于Codeforces中的前缀和相关题目及其解题思路 #### 什么是前缀和前缀和是一种常见的算法技巧,用于加速区间求和操作。对于数组 `A` 的前缀和定义如下: \[ \text{prefix}[i] = \sum_{j=1}^{i} A[j], \quad (1 \leq j \leq i) \] 利用前缀和可以快速计算任意区间的和 \( S[l..r] = \text{prefix}[r] - \text{prefix}[l-1] \)[^5]。 --- #### Codeforces 中涉及前缀和的经典题目及解法 ##### **题目一:Codeforces1398C Good Subarrays** 该题要求统计满足特定条件的连续子序列数量。具体来说,给定一个长度为 \( n \) 的整数序列 \( a_1, a_2, ..., a_n \),找出所有满足以下条件的子序列数目: \[ \sum_{i=l}^{r} a_i = r - l + 1 \] ###### 解题思路 为了高效解决此问题,可以通过构建前缀和数组并记录其差值频率的方式完成。设当前遍历到位置 \( i \),则目标转化为寻找之前某个位置 \( j \) 满足: \[ \text{prefix}[i] - \text{prefix}[j-1] = i - j + 1 \] 即: \[ \text{prefix}[i] - i = \text{prefix}[j-1] - (j-1) \] 因此可以用哈希表存储每种可能的 \( \text{prefix}[k] - k \) 出现次数,在线性时间内完成计数。 ```cpp #include <bits/stdc++.h> using namespace std; int main(){ int n; cin >> n; vector<int> a(n); for(auto &x : a) cin >> x; long long res = 0; unordered_map<long long, int> freq; freq[0]++; long long prefix_sum = 0; for(int i=0;i<n;i++){ prefix_sum += a[i]; res += freq[prefix_sum - (i+1)]; freq[prefix_sum - (i+1)]++; } cout << res; } ``` --- ##### **题目二:CodeForces21C - Stripe 2** 在这道题中,我们需要判断是否存在一种分割方式使得整个数组被划分为三个部分,每一部分的元素之和相同。 ###### 解题思路 首先验证总和是否能被三整除。如果不能,则直接返回零作为答案;否则继续尝试找到两个合适的切割点使两段分别等于总和三分之一即可[^4]。 我们可以借助两次扫描的方法来实现这一逻辑——第一次从前向后累积直到达到期望的目标值为止;第二次再重复类似过程但要跳过已选区域之外的部分。 ```cpp #include<stdio.h> const int MAXN=1e5+7; long long sum[MAXN]; int main(){ int n,a; while(~scanf("%d",&n)){ memset(sum,0,sizeof(sum)); bool flag=false; int cnt=0; for(int i=1;i<=n;++i){ scanf("%d",&a); sum[i]=sum[i-1]+a; } if(sum[n]%3!=0 || n<3){ printf("0\n"); continue; } long long target=sum[n]/3; for(int i=1;i<=n && !flag;i++)if(sum[i]==target)cnt++,flag=true; flag=false; for(int i=n;i>=1 && !flag;i--)if(sum[i]==2*target)cnt*=1,flag=true; printf("%lld\n",cnt); } return 0; } ``` --- ##### **其他常见应用场景** 除了上述例子外,还有许多场景适合应用前缀和: 1. **最大/最小子数组和** 使用 Kadane's Algorithm 结合动态规划思想可有效解答此类最优化问题。 2. **多维范围查询** 对二维甚至更高维度矩阵扩展基础概念形成积木式解决方案,比如二维前缀和能够支持矩形区域内数值迅速获取。 3. **滑动窗口技术配合使用** 当遇到固定大小或者变化不定边界约束下的局部性质分析时尤为有用。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值