当前位置: 首页 > news >正文

P14262 [ROI 2015 Day1] 自动好友

题目传送门

我的博客-欢迎光顾

写了一个很另类的容斥。。。比其他dalao的做法复杂不少


(为了方便描述,如果 \(i,j\) 是一对潜在好友,我们就称 \((i,j)\) 是一对朋友对)

(以下的 \(a,b,c\) 均指题目中的三个属性)

首先我们可以枚举两个人并判断它们能否构成一组潜在好友。时间复杂度 \(O(n^2)\)

然后,我们注意到这个题值域很小。于是本人就想着,依次算出 \(a\) 相同而 \(b,c\) 不同的朋友对, \(b\) 相同而 \(a,c\) 不同的朋友对,以及 \(c\) 相同而 \(a,b\) 不同的朋友对。最终答案就是三个朋友对的个数相加(显然不会有重复统计的朋友对)。

求这三种朋友对的方法都差不多。下面我们展开说说如何求 \(a\) 相同而 \(b,c\) 不同的朋友对。(其余同理)

我们可以将原来的人按 \(a\) 属性从小到大排序,这样 \(a\) 相同的人一定是在一个连续区间内的。

如果本题只有两个属性(仍然要求只有一个属性相同),那我们可以考虑记录一下当前 \(a\) 相同的人的左端点编号 \(l\) ,以及一个桶 \(mpb_{i}\) ,记录a属性的值落在这个区间的基础上,b属性为i的人有几个

这样,我们从左往右遍历 \(i\)\(i\) 对答案的贡献就是 \(i-l-mpb_{b_{i}}\)

现在变成三个属性了,怎么办?这个时候容斥就闪亮登场了。

沿用刚才的思路,我们用三个桶,一个桶 \(mpb_{i}\) ,记录a属性的值落在这个区间的基础上,b属性为i的人有几个;一个桶 \(mpc_{j}\) ,记录a属性的值落在这个区间的基础上,c属性为j的人有几个;一个桶 \(mp_{i,j}\) ,记录a属性的值落在这个区间的基础上,b属性为i的人且c属性为j的人有几个

这样, \(i\) 前面 \(a\) 相同但不满足条件的人的数量 \(num\) 就是 \(mpc_{c{i}}+mpb_{b{i}}-mp_{b_{i},c_{i}}\)\(i\) 对答案的贡献就是 \(i-top-num\)

这样我们就能求 \(a\) 相同而 \(b,c\) 不同的朋友对了。同理处理另两种情况即可。

代码实现的时候注意,如果进入了一个新的区间,那么 \(l\) 应当重新赋值,且 你开的几个桶都要初始化为 0 。

\(mp\) 这个桶如果不开 \(a\) 属性的这一位,那每次进入新区间都要初始化,很容易 TLE 。这里我采用了一个空间换时间的策略,给 \(mp\) 数组新开 \(a\) 的一维,这样我们只需要在处理一个大情况前初始化即可。

代码:

P14262正解
#include<bits/stdc++.h>
#define int long long
using namespace std;inline int read(){int x=0,f=1;char c=getchar();while(c<48){if(c=='-') f=-1;c=getchar();}while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f;
}inline void write(int x){if(x<0) putchar('-'),x=-x;if(x<10) putchar(x+'0');else write(x/10),putchar(x%10+'0');
}const int N=1e5+5;
const int M=105;
int n,ans,mp1[M],mp2[M],mp3[M],mp[M][M][M];
//mp1,mp2,mp3:等同于题解中的mpa,mpb,mpc 
struct sw{int b[4];
}a[N];inline bool cmp1(sw x,sw y){return x.b[1]<y.b[1];
}inline bool cmp2(sw x,sw y){return x.b[2]<y.b[2];
}inline bool cmp3(sw x,sw y){return x.b[3]<y.b[3];
}inline void solve(int id){//mp桶的初始化 for(int i=1;i<=100;i++){for(int j=1;j<=100;j++){for(int k=1;k<=100;k++){mp[i][j][k]=0;}}}if(id==1){int top=0;//top:等同于题解中的l sort(a+1,a+n+1,cmp1);for(int i=1;i<=n;i++){int x=a[i].b[1],y=a[i].b[2],z=a[i].b[3];if(a[i].b[id]!=a[i-1].b[id]){//不相同则进入新区间 for(int j=1;j<=100;j++){mp2[j]=mp3[j]=0;}top=i;//记录一下左端点 }else{int x=a[i].b[1],y=a[i].b[2],z=a[i].b[3];//桶题解 int num=mp2[y]+mp3[z]-mp[x][y][z];ans=ans+(i-top-num);}//更新一下桶 mp2[y]++;mp3[z]++;mp[x][y][z]++;}}if(id==2){//注释见上 int top=0;sort(a+1,a+n+1,cmp2);for(int i=1;i<=n;i++){int x=a[i].b[1],y=a[i].b[2],z=a[i].b[3];if(a[i].b[id]!=a[i-1].b[id]){for(int j=1;j<=100;j++){mp1[j]=mp3[j]=0;}top=i;}else{int num=mp1[x]+mp3[z]-mp[x][y][z];ans=ans+(i-top-num);}mp1[x]++;mp3[z]++;mp[x][y][z]++;}}if(id==3){//注释见上 int top=0;sort(a+1,a+n+1,cmp3);for(int i=1;i<=n;i++){int x=a[i].b[1],y=a[i].b[2],z=a[i].b[3];if(a[i].b[id]!=a[i-1].b[id]){for(int j=1;j<=100;j++){mp2[j]=mp1[j]=0;}top=i;}else{int x=a[i].b[1],y=a[i].b[2],z=a[i].b[3];int num=mp2[y]+mp1[x]-mp[x][y][z];ans=ans+(i-top-num);}mp2[y]++;mp1[x]++;mp[x][y][z]++;}}
}signed main(){n=read();for(int i=1;i<=n;i++){a[i].b[1]=read(),a[i].b[2]=read(),a[i].b[3]=read();}solve(1);solve(2);solve(3);printf("%lld",ans);return 0;
}

另,如果你有对拍的需求,也欢迎你用如下代码与你的正解对拍(即 \(O(n^2)\) 代码):

P14262暴力
#include<bits/stdc++.h>
#define int long long
using namespace std;inline int read(){int x=0,f=1;char c=getchar();while(c<48){if(c=='-') f=-1;c=getchar();}while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f;
}inline void write(int x){if(x<0) putchar('-'),x=-x;if(x<10) putchar(x+'0');else write(x/10),putchar(x%10+'0');
}const int N=1e5+5;
int n,a[N],b[N],c[N];signed main(){n=read();for(int i=1;i<=n;i++){a[i]=read(),b[i]=read(),c[i]=read();}int ans=0;for(int i=1;i<=n;i++){for(int j=1;j<i;j++){if((a[i]==a[j]&&b[i]!=b[j]&&c[i]!=c[j])||(a[i]!=a[j]&&b[i]==b[j]&&c[i]!=c[j])||(a[i]!=a[j]&&b[i]!=b[j]&&c[i]==c[j])){ans++;}}}printf("%lld",ans);return 0;
}

如果你觉得这篇题解还不错的话,记得留下你的赞呀qwq

http://www.jsqmd.com/news/17795/

相关文章:

  • 傻瓜式处理kauditd0病毒程序记录
  • win10 升级 win11 后时间更新失败
  • 软件工程学习日志2025.10.20
  • P14254 分割(树上计数问题) 题解
  • 完整教程:开源 C++ QT QML 开发(一)基本介绍
  • 102302104刘璇综合实践作业任务一:智能购物平台用户需求调研分析报告——基于195份问卷的用户痛点挖掘
  • 软件工程第二次团队作业
  • Hands on Deep Learning Chapter 3 线性神经网络
  • 超越技术范畴:低代码如何重塑企业数字文化
  • 好用的网址
  • 【C++实战(71)】解锁C++音视频编写:FFmpeg从入门到实战
  • 20251020
  • 低代码赋能业务创新:打破数字鸿沟,释放业务潜能
  • 【大模型】大模型训练的几个不同阶段
  • 详细介绍:1、手把手教你入门设计半桥LLC开关电源设计,LLC谐振腔器件计算
  • 十六天
  • 10/20/2025杂题 关于在线性时间内求解低次多项式的幂
  • 歌手与模特儿
  • 20251019
  • 计算机毕业设计 基于EChants的海洋气象数据可视化平台设计与建立 Python 大数据毕业设计 Hadoop毕业设计选题【附源码+文档报告+安装调试】
  • https://www.luogu.com.cn/problem/CF1635E
  • ZR 2025 NOIP 二十连测 Day 5
  • 关于单片机内部ADC采样率,采样精度的理解与计算整理 - 实践
  • 整体架构与数据流
  • 【上青了】
  • [VIM] reverse multiple lines in VIM
  • DeviceNet 转 Ethernet/IP:三菱 Q 系列 PLC 与欧姆龙 CJ2M PLC 在食品饮料袋装生产线包装材料余量预警的通讯配置案例
  • 【大模型】【扫盲】几种不同的微调方法
  • Tuack 生成比赛题目 PDF 笔记
  • 在 wrapper 类里实现重载方法