Codeforces Round 932 (Div. 2) D. Exam in MAC【正难则反+容斥原理】

原题链接:https://codeforces.com/problemset/problem/1935/D

题目描述:

硕士生援助中心公布了入学考试,考试内容如下。

给考生一个大小为 n 的集合 s 和一个奇怪的整数 c 。对于这个集合,需要计算出使 0≤x≤y≤c, x+y 包含在集合 s 中,并且 y−x也 包含在集合 s 中的整数对(x,y)的数目。

你的朋友想进入中心工作。请帮助他通过考试!

输入

每个测试由多个测试用例组成。第一行包含一个整数 t ( 1≤t≤2⋅10^4 ) - 测试用例的个数。测试用例说明如下。

每个测试用例的第一行包含两个整数 n 和 c ( 1≤n≤3⋅10^5 , 1≤c≤10^9 )--集合的大小和奇异整数。

每个测试用例的第二行包含 n 个整数 s1,s2,...,sn ( 0≤s1<s2<...<sn≤c )--集合的元素。( 0≤s1<s2<...<sn≤c ) - 集合 s 的元素。

保证所有测试用例中 n 的总和不超过 3⋅10^5

输出

对于每个测试用例,输出一个整数 - 合适的整数对的数目。

bash 复制代码
input
8
3 3
1 2 3
1 179
57
4 6
0 3 5 6
1 1
1
5 10
0 2 4 8 10
5 10
1 3 5 7 9
4 10
2 4 6 7
3 1000000000
228 1337 998244353
output
3
16139
10
2
33
36
35
499999998999122959

解题思路:

首先如果直接计算x+y不属于s和y-x不属于s貌似不太好计算, 这个时候就可以考虑正难则反了,ans=cnt(x,y)-cnt(x+y)-cnt(y-x)+cnt(x+y,y-x),其中ans表示答案,

cnt(x,y)表示所有的整数对(x,y)的数目,由于0<=x<=y<=c,所有总共的整数对一共有(c+1)*(c+2)/2。

cnt(x+y)表示x+y属于s的整数对的数目,那么对于0<=x<=floor(si/2)的所有x都有一个对应的y满足要求,那么就有从floor(si/2)+1个满足要求的整数对,floor表示向下取整。

cnt(y-x)表示y-x属于s的整数对的数目,那么对于si<=y<=c的所有y都有一个对应的x满足要求,那么就有(c-si+1)个满足要求的整数对。

cnt(x+y,y-x)表示x+y和x-y都属于s的所有整数对(x,y),那么不妨设x+y=si,y-x=sj,那么x=(si-sj)/2,y=(si+sj)/2,由于x和y都要求是整数,根据上述俩个式子可以得出,也就是要求si和sj的奇偶性相同即可,假设s中有odd个奇数,有even个偶数,那么这种情况满足要求的整数对数目就是(odd)*(odd+1)/2+even*(even+1)/2。

至于上述计算公式为什么最后还要加上cnt(x+y,y-x),这是因为容斥原理,我们把x+y属于s的部分减掉一遍,然后又把y-x属于s的减掉一遍,就会把x+y和y-x同时属于s的部分减掉俩遍,所有这部分还要加上一次,也就是还要加上一个cnt(x+y,y-x)。
时间复杂度:O(n),n表示输入数组的长度。

空间复杂度:不考虑输入的数组,空间复杂度O(1)。

cpp代码如下:

cpp 复制代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int N = 3e5 + 10;
int T, n, c;
int s[N];

void solve()
{
    cin >> n >> c;
    for (int i = 1; i <= n; i++)
        cin >> s[i];
    LL ans = 1ll * (c + 1) * (c + 2) / 2; // 总共的的(x,y)整数对数目
    int odd = 0, even = 0;                // odd记录s中的奇数个数,even记录s中的偶数个数
    for (int i = 1; i <= n; i++)
    {
        ans -= s[i] / 2 + 1; // cnt(x+y)
        ans -= c - s[i] + 1; // cnt(y-x)
        if (s[i] % 2 == 0)
            even++;
        else
            odd++;
    }
    // cnt(x+y,y-x)
    ans += 1ll * (even + 1) * (even) / 2;
    ans += 1ll * (odd + 1) * (odd) / 2;
    cout << ans << '\n';
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> T;
    while (T--)
    {
        solve();
    }
    return 0;
}
相关推荐
自然常数e20 分钟前
深入理解指针(6)
c语言·数据结构·算法·visual studio
Xの哲學20 分钟前
Linux VxLAN深度解析: 从数据平面到内核实现的全面剖析
linux·服务器·算法·架构·边缘计算
TL滕23 分钟前
从0开始学算法——第十八天(分治算法练习)
笔记·学习·算法
月明长歌37 分钟前
【码道初阶】【LeetCode 958】判定完全二叉树:警惕 BFS 中的“管中窥豹”陷阱
算法·leetcode·宽度优先
一直都在5721 小时前
数据结构入门:二叉排序树的构建与相关算法
数据结构·算法
_Minato_2 小时前
数据结构知识整理——复杂度的计算
数据结构·经验分享·笔记·算法·软考
listhi5202 小时前
针对燃油运输和车辆调度问题的蚁群算法MATLAB实现
前端·算法·matlab
月明长歌2 小时前
【码道初阶】【LeetCode 102】二叉树层序遍历:如何利用队列实现“一层一层切蛋糕”?
java·数据结构·算法·leetcode·职场和发展·队列
星诺算法备案2 小时前
读懂大模型备案流程,开启技术安全应用新征程
人工智能·算法·推荐算法·备案
Loo国昌2 小时前
大型语言模型推理范式演进:从提示工程到思维算法
人工智能·算法·语言模型·自然语言处理