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 

输出

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

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代码如下:

#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;
}
  • 16
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值