(遇一道特殊的补一道吧,先把我遇到的放上来)
虽然感觉差分也不算难的算法,但也开个专题吧先,有备无患
以下是几道典题
A 重新排序
此题来自y总的寒假每日一题(花1元买的课,Acwing4655,2023年1月7日的每日一题)
给定一个数组 A 和一些查询 Li,Ri,求数组中第 Li 至第 Ri个元素之和。
小蓝觉得这个问题很无聊,于是他想重新排列一下数组,使得最终每个查询结果的和尽可能地大。
小蓝想知道相比原数组,所有查询结果的总和最多可以增加多少?
输入格式
输入第一行包含一个整数 n
。
第二行包含 n
个整数 A1,A2,⋅⋅⋅,An
,相邻两个整数之间用一个空格分隔。
第三行包含一个整数 m
表示查询的数目。
接下来 m
行,每行包含两个整数 Li、Ri
,相邻两个整数之间用一个空格分隔。
输出格式
输出一行包含一个整数表示答案。
数据范围
对于 30%
的评测用例,n,m≤50;
对于 50% 的评测用例,n,m≤500;
对于 70% 的评测用例,n,m≤5000;
对于所有评测用例,1≤n,m≤105,1≤Ai≤106,1≤Li≤Ri≤n。
输入样例:
5
1 2 3 4 5
2
1 3
2 5
输出样例:
4
样例解释
原来的和为 6+14=20
,重新排列为 (1,4,5,2,3) 后和为 10+14=24,增加了 4。
思路:没啥好讲的,前缀和求原来的总和+差分+排序
#include <bits/stdc++.h>
using namespace std;
#define int long long
struct node {
int x;
int y;
};
bool cmp(node m, node n) {
return m.x < n.x;
}
signed main() {
int n;
cin >> n;
int a[n + 5];
struct node s[n + 5];
int sum[n + 5];
memset(s, 0, sizeof(s));
sum[0] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (i == 1)
sum[i] = a[i];
else
sum[i] = sum[i - 1] + a[i];
}
int m;
cin >> m;
int ans1 = 0;
while (m--) {
int l, r;
cin >> l >> r;
ans1 += sum[r] - sum[l - 1];
s[l].x++;
s[r + 1].x--;
}
for (int i = 1; i <= n; i++) {
s[i].x += s[i - 1].x;
s[i].y = i;
}
int ans2 = 0;
sort(s + 1, s + n + 1, cmp);
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
ans2 += s[i].x * (a[i]);
}
cout << ans2 - ans1 << endl;
}
B 汇编语言会编了
应该是这个题(来自女生赛训练题一B,题面已经打不开了,能打开我再补题干)
void fun(int l,int r,int x){
s[l]+=x;
s[r+1]-=x;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n,t,x;
cin>>n>>t;
fer(i,1,n){
cin>>a[i];
}
while(t--){
int x;
cin>>x;
if(x==1){
int l,r,m;
cin>>l>>r>>m;
fun(l,r,m);
}
else if(x==2){
int l,r,m;
cin>>l>>r>>m;
fun(l,r,-m);
}
else if(x==3){
int c;
cin>>c;
a[c]-=s[c+1];
a[c+1]+=s[c];
swap(a[c],a[c+1]);
swap(s[c],s[c+1]);
}
}
fer(i,1,n){
s[i+1]+=s[i];
}
fer(i,1,n){
cout<<a[i]+s[i]<<" ";
}
}
C Complete the Sequence
题意:给定一个长度为s的数列a1,a2,a3,……,as,并知道它的通项可以用多项式P(n)表示出来,求数列的后c项。 思路:说实话看见这个我还以为要用线代了(害pia),然后这个题的思路也是差分
![](https://img-blog.csdnimg.cn/img_convert/b8d8895065669e1ca280507f1c2f8436.jpeg)
把n-1阶差分补完(用0)补,然后向上递推回去,a[i][j]=a[i+1][j-1]+a[i][j-1]
![](https://img-blog.csdnimg.cn/img_convert/d339658fa978cace4267e2c081757f87.jpeg)
![](https://img-blog.csdnimg.cn/img_convert/f290b2cf685cd5fdb089d735331b65c8.jpeg)
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=110;
int a[maxn][maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=0; i<n; i++)
scanf("%d",&a[0][i]);
for(int i=1; i<n; i++)
for(int j=0; j<n-i; j++)
a[i][j]=a[i-1][j+1]-a[i-1][j];
for(int i=1; i<m+n; i++)
a[n-1][i]=a[n-1][0];
for(int i=n-2; i>=0; i--)
{
for(int j=n-i; j<n+m; j++)
{
a[i][j]=a[i+1][j-1]+a[i][j-1];
}
}
for(int i=n;i<n+m;i++)
printf("%d ",a[0][i]);
printf("\n");
}
}