1.行列相等
1.1.题目
描述
给定一个 n 行 m 列的整数矩阵 a,请统计满足以下条件的行列对数量:- 第 i 行所有元素之和恰好等于第 j 列所有元素之和。
输入
在一行上输入两个整数 n,m (1≤n,m≤1e6;n×m≤1e6),表示矩阵的行数和列数;此后 n 行,每行输入 m 个整数 ai,1,ai,2,...,ai,m (0≤ai,j≤1e9),表示矩阵中各元素。
输出
输出一个整数,表示满足条件的行列对数量。
用例输入 1
3 3
1 1 1
1 1 1
1 1 1
用例输出 1
9
1.2.题解
- 哈希表计数 :用
unordered_map存储键为和值 ,值为pair<行出现次数, 列出现次数> - 先算行:遍历所有行,计算行和,更新哈希表中行的计数
- 再算列:遍历所有列,计算列和,更新哈希表中列的计数
- 统计答案 :遍历哈希表,累加
行计数 × 列计数得到总数量
**时间复杂度:**O(n*m)
**空间复杂度:**O(n*m)
AC代码:
cpp
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ll n,m;
cin>>n>>m;
unordered_map<ll,pair<ll,ll>> sumMapCnts;
vector<vector<ll>> ma(n,vector<ll>(m));
//计算每行的和并统计到哈希表
for(ll i=0;i<n;++i)
{
ll sum=0;
for(ll j=0;j<m;++j)
{
cin>>ma[i][j];
sum += ma[i][j];
}
++sumMapCnts[sum].first;
}
//计算每列的和并统计到哈希表
for(ll j=0;j<m;++j)
{
ll sum=0;
for(ll i=0;i<n;++i)
{
sum += ma[i][j];
}
++sumMapCnts[sum].second;
}
ll ans=0;//保存答案
for(const auto &[sum,p]:sumMapCnts)
{
ans += p.first*p.second;
}
cout<<ans<<endl;
return 0;
}
2.乱翘的数组hard
2.1.题目
描述
对于给定的长度为 n 的数组 {a1,a2,...,an},我们定义"翘数"为同时严格大于或小于左右相邻数的数字。形式化的讲,对于第 i (1<i<n) 个整数 ai,它被称作"翘数",当且仅当满足 ai−1 < ai > ai+1 或 ai−1> ai < ai+1。 若一个数组中,所有的满足 i∈(1,n) 的数字 ai 均为"翘数",且任意相邻的两个元素 aj,aj+1 (1≤j<n) 都不相等,则称该数组为"乱翘的数组"。 现在,对于给定的初始数组,计算最少需要从原数组中删除的数字个数,使得剩余数字按原相对顺序拼接成的新数组是一个"乱翘的数组"。
输入
第一行输入一个整数 n (3≤n≤2×1e5) 代表数组中的元素数量。 第二行输入 n 个整数 a1,a2,...,an (−1e7≤ai≤1e7) 代表数组元素。
输出
在一行上输出一个整数,代表最少需要删除的数字个数。
用例输入 1
7
1 3 1 4 5 2 0
用例输出 1
2
用例输入 2
3
2 2 2
用例输出 2
2
2.2.题解
把数组作为折线图来看,
其实就是要删除除了顶峰和谷底的所有元素
如下图所示,每个绿色圆圈代表一个需要保留的数,非绿色圆圈的数都是要被删除的

那如果有连续相等的数呢?
那就需要视情况提前删除掉一些,因为连续相等的数不符合翘数的要求,并且提前删除代码也好写
**时间复杂度:**O(n)
**空间复杂度:**O(n)
AC代码:
cpp
#include <iostream>
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
ll n;
cin>>n;
vector<ll> arr;
ll ans=0;//ans为最终需要删除的数
for(ll i=0;i<n;++i)
{
ll num;cin>>num;
if(!arr.empty() && arr.back()==num) ++ans;
else arr.emplace_back(num);
}
ll cnt=0; //cnt统计需要保留的数,即上图绿色圆圈代表的数
for(ll i=0;i<arr.size();++i)
{
//如果是峰顶
if((i-1>=0 && arr[i-1]<arr[i] || i-1<0) && (i+1<arr.size() && arr[i]>arr[i+1] || i+1>=arr.size())) ++cnt;
//如果是谷底
else if((i-1>=0 && arr[i-1]>arr[i] || i-1<0) && (i+1<arr.size() && arr[i]<arr[i+1] || i+1>=arr.size())) ++cnt;
}
ans += arr.size()-cnt;
cout<<ans<<endl;
return 0;
}
3.树上异或路径
3.1.题目
描述
给你一棵有 n 个节点的无向树。每条边有一个非负整数权值 w。请计算这棵树上所有简单路径的异或和之和。注意,本题中树为无向图,端点 (u,v) 与 (v,u) 视为同一路径,仅统计一次。说明:路径指的是在树上选择两个节点作为端点的简单路径。当两个端点相同的时候,路径长度为 0,其异或和为 0。
输入
每个测试文件均包含多组测试数据。第一行输入一个整数 T (1≤T≤2×1e5) 表示数据组数。除此之外,保证单个测试文件的 n 之和不超过 5×1e5。每组测试数据的格式如下:第一行输入一个整数 n (1≤n≤2×1e5),表示节点数量;此后 n−1 行,每行输入三个整数 u,v,w (1≤u,v≤n;0≤w≤1e9),表示一条连接 u 与 v 的边及其权值。保证给出的是一棵树。
输出
对于每一组测试数据,新起一行输出一个整数,表示所有简单路径的异或和之和。若结果可能很大,请将答案对 1e9+7 取模后输出。
用例输入 1
2
3
1 2 1
2 3 2
4
1 2 0
2 3 0
3 4 7
用例输出 1
6
21
3.2.题解
首先假设节点1是整棵树的根,再开辟一个大小为n的数组rootXor,
其中rootXor[i]表示从根节点到i节点的简单路径上经过的所有权值的异或和
计算rootXor可以通过dfs和邻接表来算
此时任意两个节点u,v的简单路径的异或和都可以通过rootXor来计算,
等于rootXor[u] ^ rootXor[v] (因为异或有一个性质,a^a=0).
那么此时整个问题就变为:从rootXor数组中任意挑出两个不相同的位置i,j(i<j)的元素组成元素对,求所有元素对的异或结果的和
用数学公式表示为:
如果直接暴力计算时间复杂度就是O(n^2),肯定不行
此时异或还有一个性质,即每一个二进制位的计算互不影响,
所以我们可以分开求每一个二进制位对答案S的贡献,再累加求和即可;
那么如何求某一个二进制位对答案S的贡献?
我们可以统计rootXor数组中出有多少个数在这一二进制位上的值为1(计为cnt1),有多少个数在这一二进制位上的值为0(计为cnt0);
然后 cnt1*cnt0*这一个二进制位在十进制内的权值 即为该二进制位对答案S的贡献(cnt1*cnt0 是求 在这个二进制位上进行异或计算会出现多少次1)
至此,思路完毕
**时间复杂度:**O(nlogU),n为点的个数,U为权重最大值的二进制位数
**空间复杂度:**O(n)
AC代码:
cpp
#include <iostream>
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int main() {
const ll mod=1e9+7;
ll t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
//邻接表,ad[i]:保存所有与i节点有边连接的点
vector<vector<pair<ll,ll>>> ad(n);
for(int i=1;i<=n-1;++i)
{
ll u,v,w;
cin>>u>>v>>w;
--u,--v;//作用是让节点从0开始计数,而不是题目的从开始
ad[u].emplace_back(v,w);
ad[v].emplace_back(u,w);
}
//rootXor[i]:从根节点到i节点的简单路径上经过的所有权值的异或和
vector<ll> rootXor(n);
//通过dfs计算rootXor内所有元素的值,时间复杂度为O(n)
function<void(ll,ll)> dfs;
dfs=[&](ll pa,ll cur)
{
for(auto &[child,w]:ad[cur])
{
if(pa!=child)
{
rootXor[child]=rootXor[cur]^w;
dfs(cur,child);
}
}
};
dfs(-1,0);
ll ans=0; //保留答案
for(ll i=0;i<=30;++i)
{
//计算cnt0和cnt1,含义如上述文字
ll cnt0=0,cnt1=0;
for(auto &e:rootXor)
{
if((e>>i)&1) ++cnt1;
else ++cnt0;
}
//累加和到ans上
ans=(ans%mod+(((1ll<<i)*cnt0*cnt1)%mod)%mod)%mod;
}
cout<<ans<<endl;
}
return 0;
}