1 条题解

  • 0
    @ 2025-4-13 15:48:54

    根据题意,易得下面两个计算公式:

    1. 当前数字小于等于转经的次数,则贡献为:(ai+1)ai/2;(a_{i} + 1) *a_{i}/2;
    2. 当前数字大于转经的次数,则贡献为:(ai2r+1)r/2;(a_{i} *2-r+1) *r/2;

    经过观察,排序并不改变功德值计算,因为每个数字都可以独立计算的。我们可以先对原始数组从小到大进行排序,然后根据当前的转经的次数二分找到数组的下标,使得下标左侧(包含)均为①式,下标右侧均为②式。

    前者我们可以使用前缀和预处理①式即可 O(1)O(1) 计算,后者我们需要推出下面公式。

    $$(a_{r+1} *2-r+1) *r/2 + (a_{r+2} *2-r+1) *r/2 + ... + (a_{n} *2-r+1) *r/2\\ = [(a_{r+1}+a_{r+2}+...+a_{n}) *2-(r-1)*(n-r)] *r/2\\ = [\sum_{i=r+1}^n (a_{i})*2-(r-1)*(n-r)] *r/2 $$

    预处理后缀和,我们即可 O(1)O(1) 计算后者。

    $$ans =\sum_{i=1}^r [(a_{i} + 1) *a_{i}/2] +[\sum_{i=r+1}^n (a_{i})*2-(r-1)*(n-r)] *r/2 $$

    时间复杂度:O(nlogn+klogn)O(nlogn+klogn)

    const int maxn = 2e5 + 7;
    int n, k, a[maxn], b[maxn], c[maxn], mod = 1e9 + 7;
    
    int ksm(int a, int b)
    {
    	int res = 1;
    	while (b)
    	{
    		if (b % 2)
    			res = res * a % mod;
    		b /= 2;
    		a = a * a % mod;
    	}
    	return res;
    }
    
    void solve()
    {
    	int n, k;
    	cin >> n >> k;
    
    	int two = ksm(2, mod - 2);
    
    	for (int i = 1; i <= n; i++)
    		cin >> a[i];
    
    	sort(a + 1, a + 1 + n);
    	for (int i = 1; i <= n; i++)
    	{
    		b[i] = ((((a[i] + 1) * a[i] % mod) * two % mod) + b[i - 1]) % mod;
    		c[i] = (a[i] + c[i - 1]) % mod;
    	}
    
    	for (int i = 1; i <= k; i++)
    	{
    		int x;
    		cin >> x;
    		int l = 0, r = n;
    		while (l < r)
    		{
    			int mid = (l + r + 1) / 2;
    			if (a[mid] <= x)
    				l = mid;
    			else
    				r = mid - 1;
    		}
    
    		int tmp = ((((c[n] - c[l]) * 2 - (x - 1) * (n - l)) % mod) + mod) % mod;
    		int ans = (b[l] + (tmp * x % mod) * two) % mod;
    		cout << ans << endl;
    	}
    }
    
    • 1

    信息

    ID
    3
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    3
    已通过
    3
    上传者