在算法竞赛问题中,手推公式是一项及其重要的技能,下至签到题,上至高级数学、数据结构、动态规划,都需要扎实的推公式能力。
本文章先从简单题入手,总结常见的推公式思路。
例题1:乘法的妙用
题目来源:
https://codeforces.com/contest/1715/problem/C
题意:
记
g
(
l
,
r
)
g(l,r)
g(l,r)为区间中不同连续相等子区间的个数,求
∑
l
=
1
n
∑
r
=
l
n
g
(
l
,
r
)
\sum_{l=1}^{n} \sum_{r=l}^{n} g(l,r)
∑l=1n∑r=lng(l,r)
思路:
朴素的做法是枚举区间右端点,算出
∑
g
(
l
,
r
i
)
\sum g(l,r_i)
∑g(l,ri)值,依次相加。这样做很容易发现
r
i
r_i
ri和
r
i
−
1
r_{i-1}
ri−1之间有重复计算的部分。找找规律,分类讨论
a
i
a_i
ai与
a
i
−
1
a_{i-1}
ai−1相等和不相等的情况,即可
O
(
n
)
O(n)
O(n)求出
∑
g
(
l
,
r
)
\sum g(l,r)
∑g(l,r)。
考虑
O
(
1
)
O(1)
O(1)查询,其实修改后只会影响重复运算的部分,故用乘法,加上/减去
i
×
(
n
−
i
)
i \times (n-i)
i×(n−i)即可。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5+10;
LL n,q;
LL a[maxn];
int main()
{
ios::sync_with_stdio(false);
cin>>n>>q;
for(LL i=1;i<=n;i++)
cin>>a[i];
LL add=1,ans=add;
for(LL i=2;i<=n;i++)
{
if(a[i]!=a[i-1])
add+=i;
else
add+=1;
ans+=add;
}
while(q--)
{
LL id,x;
cin>>id>>x;
if(a[id]==a[id-1] &&x!=a[id-1])
ans+=(id-1)*(n-id+1);
else if(a[id]!=a[id-1] && x==a[id-1])
ans-=(id-1)*(n-id+1);
if(a[id]==a[id+1] &&x!=a[id+1])
ans+=id*(n-id);
else if(a[id]!=a[id+1] && x==a[id+1])
ans-=id*(n-id);
cout<<ans<<'\n';
a[id]=x;
}
return 0;
}
例题2:结合差分,前缀和
若推出的公式会TLE,要么用其他技巧变换公式,要么用数据结构优化(后面会提及),要么差分/前缀和。
题目来源:
https://codeforces.com/contest/1715/problem/C
题意:
求矩阵中相同元素的曼哈顿距离之和。
思路:
朴素的做法是对于每一种元素,枚举每一列/行,个数
×
\times
×距离作累加,显然会TLE。
考虑注意到各个元素的列数可以预处理出来,因此求距离可以采用距离总和—距离前缀和的方式。
编程时,可以将相同元素的列数存放到一起,排序,作为前缀和数组。
code:
#include <bits/stdc++.h>
using namespace std;
const int N=500;
const int M=1e5+10;
typedef pair<int,int> PII;
typedef long long ll;
int n,m;
int a[N][N];
vector <int> row[M];
vector <int> col[M];
ll res;
ll cal(vector<int> a)
{
ll sum=0,k=a.size();
sort(a.begin(),a.end());
vector<ll> S(k+1);
for(int i=1;i<=k;i++)
S[i]=S[i-1]+a[i-1];
for(int i=1;i<=k;i++)
sum+=(ll)(i-1)*a[i-1]-S[i-1];
return sum;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
row[a[i][j]].push_back(i);
col[a[i][j]].push_back(j);
}
}
for(int color=1;color<=1e5;color++)
{
res+=cal(row[color])+cal(col[color]);
}
cout<<res<<endl;
return 0;
}