每日一题~cf650 A(洛谷 wathman)容斥

洛谷
在这里插入图片描述
在这里插入图片描述
依次为曼哈顿距离,欧式距离的公式。
公式化简一下 就知道,我们要找 横坐标相等 或者 纵坐标相等的 一对点。
所以开两个map,一个记录横坐标的个数,一个记录纵坐标的个数。遍历点。
对于每一个点
ans += mp1[ve[i].first];
ans += mp2[ve[i].second];
mp1[ve[i].first]++;
mp2[ve[i].second]++;
这里的意思是 ,(x,y)会和前面 横坐标相同的点 组成一对,会和前面纵坐标相同的点 组成一对。但是对于 坐标相同的点,我们重复计算了,所以还要再开一个map,记录重复坐标的点的数量和信息。
最后减去,多加的就可以了。

#include <bits/stdc++.h>
using namespace std;
#define int long long 
signed main()
{
	int n; cin >> n;
	vector<pair<int, int>>ve(n);
	//点的坐标
	int ans = 0; 
	map<int, int>mp1;
	map<int, int>mp2;
	map<pair<int, int>, int>mp3;
	
	int cnt = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> ve[i].first;
		cin >> ve[i].second;
		mp3[{ve[i].first,ve[i].second}]++;
		
		ans += mp1[ve[i].first];
		ans += mp2[ve[i].second];
		mp1[ve[i].first]++;
		mp2[ve[i].second]++;
	}
	
	这里自己模拟一下,公式就推出来了
	for (auto i : mp3)
	{
		if (i.second > 1)
		{
			cnt += i.second * (i.second - 1) / 2;
		}
	}
	
	//减去两个点完全相同的个数。
	cout << ans - cnt << "\n";


}

当然也可以 直接在遍历点的时候,处理重复点的信息。
这个也许 更简单一点?

#include <bits/stdc++.h>
using namespace std;
#define int long long 
signed main()
{
	int n; cin >> n;
	vector<pair<int, int>>ve(n);
	//点的坐标
	int ans = 0; 
	map<int, int>mp1;
	map<int, int>mp2;
	map<pair<int, int>, int>mp3;
	
	int cnt = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> ve[i].first;
		cin >> ve[i].second;
		ans += mp1[ve[i].first];
		ans += mp2[ve[i].second];
        ans-=mp3[{ve[i].first,ve[i].second}];
		mp1[ve[i].first]++;
		mp2[ve[i].second]++;
        mp3[{ve[i].first,ve[i].second}]++;
	}
    cout << ans  << "\n";
	
}
	

codeforce 946 C
n个数字,定义了三元组,

询问有多少美丽对。也是容斥。

#include <bits/stdc++.h>

void solve()
{
	int n;
	std::cin >> n;

	std::vector<int>a(n);
	for (int i = 0; i < n; i++)
		std::cin >> a[i];
	long long  ans = 0;
	//两个三元组美丽,就是 只差一个。那么他们可能是ab ac bc位置是相同的。
	// 对于 每一个位置开一个map
	//枚举三元组的位置,例如abc
	//依次枚举了 ab ac bc
	//实际上 是分别枚举了 每一个三元组的 ab ac bc位置
	for (int x=0;x<3;x++)
		for (int y = x + 1; y < 3; y++) {
			std::map<std::pair<int, int>, int>cnt;
			for (int i = 0; i + 2 < n; i++) {
				//std::cout << a[i + x] << " " << a[i + y] << "\n";
				
				//每一次碰到 这个二元有序,她可以和前面的 美丽
				ans += cnt[{a[i + x], a[i + y]}];
				cnt[{a[i + x], a[i + y]}]++;
				
			}
		}
	//减去三个字符都相等的情况
	std::map<std::tuple<int, int, int>, int>cnt;
	for (int i = 0; i + 2 < n; i++)
	{
		ans -= 3*cnt[{a[i], a[i + 1], a[i + 2]}];
		cnt[{a[i], a[i + 1], a[i + 2]}]++;
	}
	std::cout << ans << "\n";
}
int  main()
{
	std::cin.tie(nullptr)->sync_with_stdio(false);
	int t; std::cin >> t;
	while (t--)
	{
		solve();
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值