AtCoder Beginner Contest 166 E This Message Will Self-Destruct in 5s 公式推导+排列组合+统计雷同元素归属不同种类的数量

AtCoder Beginner Contest 166   比赛人数11690  比赛开始后11分钟看到所有题

AtCoder Beginner Contest 166  E  This Message Will Self-Destruct in 5s   公式推导+排列组合+统计雷同元素归属不同种类的数量

总目录详见https://blog.csdn.net/mrcrack/article/details/104454762

在线测评地址https://atcoder.jp/contests/abc166/tasks/abc166_e

第一次能在赛后20分钟内,独立AC掉E题,可喜可贺。

该题目标就是寻找满足的Aj+Ai==j-i对数,其中i!=j.

将上述公式进行变形,得到Ai+i==j-Aj.

针对样例,应用上述公式

Input:
6
2 3 3 1 3 1
Output:
3

位置 1  2  3 4 5 6
数值 2  3  3 1 3 1
Ai+i 3  5  6 5 8 7
j-Aj -1 -1 0 3 2 5

雷同的3,Ai+i有1个,j-Aj有1个,对应的组合数量有1*1=1个
雷同的5,Ai+i有2个,j-Aj有1个,对应的组合数量有2*1=2个

总的数量是1+2=3

提醒,可能会遇到雷同情况时,Ai+i==j-Aj,此时i==j,此种情况,注意扣除。

数据如何处理?请接着看
Ai+i 3  5  6 5 8 7
j-Aj -1 -1 0 3 2 5
给Ai+i系列打上标记0,j-Aj系列打上标记1,将两个数组拼成一个数组,排序后,如下
位置 1     2     3    4    5    6    7    8    9    10   11   12
数值 -1(1) -1(1) 0(1) 2(1) 3(0) 3(1) 5(0) 5(0) 5(1) 6(0) 7(0) 8(0)
Ai+i 3  5  6 5 8 7
j-Aj -1 -1 0 3 2 5

如何找排列组合需要的数据,请仔细观察
3(0) 3(1)这组数据在数值相等的基础上,出现了标记0,1
5(0) 5(0) 5(1)这组数据在数值相等的基础上,出现了标记0,1

AC代码如下

#include <cstdio>
#include <algorithm>
#define maxn 200010
#define LL long long
using namespace std;
int a[maxn],b[maxn],c[maxn],cnt,cnta,cntb;
LL sum;
struct node{
	int flag;
	int v;
}e[maxn<<1];
int cmp(node a,node b){
	return a.v==b.v?a.flag<b.flag:a.v<b.v;//若数值相等,按标记自小到大排序
}
int main(){
	int n,i,good;
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	for(i=1;i<=n;i++)b[i]=a[i]+i,c[i]=i-a[i];//计算Ai+i,j-Aj
	for(i=1;i<=n;i++)
		if(b[i]==c[i])sum--;//提醒,可能会遇到雷同情况时,Ai+i==j-Aj,此时i==j,此种情况,注意扣除.
	for(i=1;i<=n;i++)//给Ai+i系列打上标记0,j-Aj系列打上标记1,将两个数组拼成一个数组
		e[++cnt].flag=0,e[cnt].v=b[i];
	for(i=1;i<=n;i++)//给Ai+i系列打上标记0,j-Aj系列打上标记1,将两个数组拼成一个数组
		e[++cnt].flag=1,e[cnt].v=c[i];
	sort(e+1,e+1+cnt,cmp);//排序
	e[0].v=2e9,cnta=0,cntb=0,good=0;
	for(i=1;i<=cnt;i++)
		if(e[i-1].v==e[i].v){
			if(e[i-1].flag==0&&e[i].flag==1)good=1;//参与排列组合的数据在数值相等的基础上,出现了标记0,1
			if(e[i].flag==0)cnta++;//统计参与排列组合的数据中b[i]=a[i]+i的数量
			else cntb++;//统计参与排列组合的数据中c[i]=i-a[i]的数量
		}else{
			if(good)sum+=(LL)cntb*(cnta+1);//cnta+1,+1是指加上参与排列组合的雷同数据的第一个,之前未参与计数
			cnta=0,cntb=0,good=0;
		}
	if(good)sum+=(LL)cntb*(cnta+1);//边界处理。
	printf("%lld\n",sum);
	return 0;
}

试了试map的写法,代码确实够短

AC代码如下

#include <cstdio>
#include <map>
#define maxn 200010
#define LL long long
using namespace std;
int a[maxn],n;
LL ans;
map<int,int> mp;
int main(){
	int i;
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&a[i]);
	for(i=1;i<=n;i++)
		if(i-a[i]>0)mp[i-a[i]]++;//记录i-a[i]的数量
	for(i=1;i<=n;i++)
		if(mp[a[i]+i])ans+=mp[a[i]+i];//查重
	printf("%lld\n",ans);
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值