记录53
#include<bits/stdc++.h>
using namespace std;
int a[2010][2010]={};
int num[15]={};
int dx[5]={0,1,0,-1};
int dy[5]={1,0,-1,0};
int main(){
int n,m,t,cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>t;
num[t]++;
a[i][j]=t;
}
}
long long ans=0;//重点:只要是累加,想想会不会很大
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cnt=0;
for(int k=0;k<4;k++){
int xn=i+dx[k];
int yn=j+dy[k];
if(a[xn][yn]==a[i][j]) cnt++;
}
int k=a[i][j];
ans+=num[k]-cnt-1;//减掉一个a[i][j]自己
}
}
cout<<ans;
return 0;
}
题目传送门
https://www.luogu.com.cn/problem/P9582
突破点
两个不同的方格不相邻,当且仅当这两个方格没有公共边
👉上下左右不相邻
两个不同的方格互为好朋友,当且仅当这两个方格不相邻且这两个方格中的数字相同
👉两个方格上下左右不相邻,同时方格内数字一样,就是这个格子的好朋友
表示所有方格的好朋友的数量之和
👉遍历所有格子
注意:分析样例发现,找到A格子是B格子的好朋友,在A格子的时候也要重新计算B格子
思路
学过dfs这样的搜索的话,这题做起来会很轻松
- 搭建坐标系👉二维数组
- 找相邻格子👉判断上下左右
- 除了当前格子跟相邻格子,地图上剩下的相同数都是当前格子的好朋友
- 计数并统计
处理思路第三步的时候,可以通过一开始就记录好地图上每个数(1~9)的和
每次只要去掉除了相邻格子内相同的数,就是这个格子的好朋友总数
代码简析
#include<bits/stdc++.h>
using namespace std;
int a[2010][2010]={};
int num[15]={};
int dx[5]={0,1,0,-1};
int dy[5]={1,0,-1,0};
int main(){
int n,m,t,cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>t;
num[t]++;
a[i][j]=t;
}
}
...
...
return 0;
}
int a[2010][2010]={}; 👉 搭建地图
int num[15]={}; 👉 数组桶存放数字(1~9)
int dx[5]={0,1,0,-1}; 👉 x偏移量
int dy[5]={1,0,-1,0}; 👉 y偏移量,二者结合,右下左上(逆时针)
int n(行),m(列),t(表格内数字),cnt=0(上下左右相同的数字);
num[t]++; 👉 桶计数
a[i][j]=t; 👉 构建地图
#include<bits/stdc++.h>
using namespace std;
int a[2010][2010]={};
int num[15]={};
int dx[5]={0,1,0,-1};
int dy[5]={1,0,-1,0};
int main(){
int n,m,t,cnt=0;
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>t;
num[t]++;
a[i][j]=t;
}
}
long long ans=0;//重点:只要是累加,想想会不会很大
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cnt=0;
for(int k=0;k<4;k++){
int xn=i+dx[k];
int yn=j+dy[k];
if(a[xn][yn]==a[i][j]) cnt++;
}
int k=a[i][j];
ans+=num[k]-cnt-1;//减掉一个a[i][j]自己
}
}
cout<<ans;
return 0;
}
long long ans=0; (所有好朋友的数) 👉 注意:只要是累加,想想会不会很大
双重for循环遍历地图
int xn=i+dx[k]; 👉 当前x坐标的偏移量
int yn=j+dy[k]; 👉 当前y坐标的偏移量
if(a[xn][yn]==a[i][j]) cnt++; 👉 统计相邻格子中的好友数
int k=a[i][j];(当前格子存储的数字)👉 找到桶下标
ans+=num[k]-cnt-1; 👉 将 这个数字存在的所有格子-相邻的好朋友-自己,然后加到ans总数
补充
CSP-J竞赛中必须开long long的场景与隐蔽陷阱汇总
一、必须开long long的八大场景(看到就开,不要犹豫)
场景1:累加和(最常见)
int a[100000]; // a[i] ≤ 1e9 ll sum = 0; // 必须用long long for (int i = 0; i < n; i++) sum += a[i]; // 最大和: 1e5 × 1e9 = 1e14 > INT_MAX (2e9)隐蔽坑:前缀和、后缀和、滑动窗口和都属此类。
场景2:乘法运算(最隐蔽)
int a = 1e5, b = 1e5; ll c = 1LL * a * b; // 必须转long long再乘 // 错误: int c = a * b; // 先int溢出,再赋值隐蔽坑:计算面积、体积、组合数、二次项时都需转。
场景3:DP状态值(最致命)
int dp[1005][1005]; // ❌ 错误:状态值可能爆int ll dp[1005][1005]; // ✓ 正确:组合数、方案数必开ll // 示例:走格子方案数 dp[i][j] = dp[i-1][j] + dp[i][j-1]; // 累加快,易溢出隐蔽坑:背包DP、LCS、走地图方案数等状态累加题。
场景4:图论路径长度
struct Edge { int to; ll w; }; // 边权必须ll vector<Edge> g[N]; dist[v] = dist[u] + w; // 累加距离,可能爆int隐蔽坑:最短路径、最小生成树边权和。
场景5:数论与组合数
ll factorial(int n) { ll res = 1; for (int i = 1; i <= n; i++) res *= i; // 阶乘增长极快 return res; } // 20! ≈ 2.4e18 > INT_MAX隐蔽坑:阶乘、排列组合、卡特兰数、模运算中间值。
场景6:时间戳与计数器
int timestamp = 0; for (int i = 0; i < 1e8; i++) { timestamp++; // 1亿次,int够用,但为安全可开ll }隐蔽坑:时间戳在长时间模拟中可能达1e9以上。
场景7:自定义结构体成员
struct Node { int id; ll val; // 累加值、权重必须ll ll sum; // 子树和必须ll };隐蔽坑:树状数组、线段树维护的和值。
场景8:前缀和数组
ll pre[N]; // 前缀和必开ll pre[i] = pre[i-1] + a[i]; // 累加易溢出隐蔽坑:二维前缀和、差分数组、树上前缀和。
二、隐蔽陷阱汇总(坑你没商量)
陷阱1:中间结果溢出(最隐蔽)
int a = 1e9, b = 1e9; ll c = a * b; // ❌ 错误:a*b先int溢出,再转ll // 正确:c = 1LL * a * b; // 先转ll再乘 // 更隐蔽的: ll d = a * b / 2; // ❌ 乘的时候已溢出 // 正确:d = 1LL * a * b / 2;检测方法:**只要有乘法,先写1LL **。
** 陷阱2:函数返回值类型 **
int calc(ll x) { // ❌ 错误:返回值int,可能溢出 return x * x; // x*x是ll,转int截断 } // 正确:ll calc(ll x) { return x * x; }** 检测方法 : 函数参数或返回值可能大,一律long long **。
** 陷阱3:三元运算符类型推导 **
int a = 1e9, b = 1e9; ll c = (a > b) ? a : b; // ✓ 正确:取较大值,不会溢出 ll d = (a > 0) ? a * b : 0; // ❌ 错误:a*b在?:内是int,溢出后再转ll // 正确:ll d = (a > 0) ? 1LL * a * b : 0;** 检测方法 : 三元运算符内有计算,先转ll**。
陷阱4:结构体初始化
struct Node { ll x, y; Node() : x(INT_MAX), y(INT_MAX) {} // ❌ 初始化int溢出 // 正确:Node() : x(LL_INF), y(LL_INF) {} };检测方法:struct成员是ll,初始化用LL_INF。
陷阱5:数组大小
ll a[1000000]; // ✓ 正确:8MB // 错误:ll a[10000000]; // 80MB,可能超内存限制检测方法:算内存:N × sizeof(ll) < 256MB × 0.8。
陷阱6:位运算与ll
ll x = 1LL << 40; // ✓ 正确:1LL是ll类型 // 错误:ll x = 1 << 40; // 1是int,左移40位溢出检测方法:左移大于30位,必须用1LL。
陷阱7:printf/scanf格式
ll x = 1e12; printf("%d\n", x); // ❌ 错误:%d只能读int // 正确:printf("%lld\n", x); // 或:cout << x; // 推荐检测方法:ll变量必须用%lld,或改用cin/cout。
三、CSP-J开long long决策树
看到变量 → 它是否参与累加/乘法/DP状态/图论距离? ├── 是 → 开long long │ └── 是否作为函数参数/返回值? │ └── 是 → 函数也开long long │ └── 否 → 是否可能累加多个int? └── 是 → 开long long铁律:不确定时,开long long永远比int安全。
多8字节内存 vs 溢出WA,选前者。
四、一句话总结
CSP-J中,累加、乘法、DP、图论、前缀和、结构体成员,一律long long;记住1LL转类型、函数返回ll、%lld输出、算内存防爆炸,开小了见祖宗!
1016

被折叠的 条评论
为什么被折叠?



