2023HUEL寒假集训: 1.10 前缀和 - Virtual Judge (vjudge.net)
#include
#include
using namespace std;
const int N = 1e5 + 100;
int a[N];
int p[N], t[N];
int x[N];
int main()
{
int n, q;
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
if (i == 1)
p[i] = a[i], t[i] = a[i];
else
p[i] = max(p[i - 1], a[i]),
t[i] = min(t[i - 1], a[i]);
}
for (int i = 1; i <= q; i++)
{
scanf("%d", &x[i]);
x[i] = p[x[i]] - t[x[i]];
}
for (int i = 1; i < q; i++)
printf("%d ", x[i]);
printf("%d", x[q]);
return 0;
}
P8649 [蓝桥杯 2017 省 B] k 倍区间 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
使用前缀和取模。
我们假设n=5;k=2;
输入的5个数为1,2,3,4,5的话
程序输出6 这6种情况分别是123 1234 2345 345 2 4;
若我们将前i项和的模存到一个数组mod[i]中,mod[1]表示前一项的和的模,mod[2]表示前两项的和的模,以此类推,再将mod[i]相同的个数用一个数组add[mod[i]]存起来,只需要计算C 2 add[i](高中数学的排列组合,2在上面,add[i]在下面)再加上add[0]的所有结果,便可求得正确答案,极大简化了运算时间。
还是上面那个例子:1 2 3 4 5 假如是输入这5个数
可得mod[1]=1(1%2=1) mod[2]=1((1+2)%2=1) mod[3]=0 mod[4]=0 mod[5]=1
mod=1的个数有3个,mod=0的个数有2个,所以add[0]=2,add[1]=3;
我们先看mod=0的时候,所对应的数是3,4,在(3,4]区间中(注意是左开右闭),4%2=0满足条件,
mod=1的时候,所对应的数是1,2,5,在(1,2]区间中,2%2=0满足条件,(1,5]区间中,2345满足条件(2+3+4+5)%2=0,在(2,5]区间中,345满足条件。
所以可以得出结论,从每一种前缀和(所选两个数的中间的数的和)的情况中任意选择两个数组成的区间都满足是k倍区间,其计算方法其实就是排列组合,上面的例子即可表示为C2 2加上C2 3结果为1+3=4,但实际结果有6种,是由于区间是左开右闭的,加上add[0]的所有结果也就是2和4两种,相当于区间左闭右闭,这两个数本身构成一个k倍区间,4+2=6,刚好是正确答案。
于是可得如下代码:
#include
using namespace std;
long long mod[100010],add[100010];
int main()
{
int n,k,a;
long long cnt=0;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a;
mod[i]=(mod[i-1]+a)%k;
add[mod[i]]++;
}
for(int i=0;i
P6568 [NOI Online #3 提高组] 水壶 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
AC Code:
#include
using namespace std;
const int N = 1e6 + 5;
int a[N], sum[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
sum[i] = sum[i - 1] + a[i];
}
int max = 0;
for (int i = 1; i + m <= n; i++)
{
int k = sum[i + m] - sum[i - 1]; // 前缀和计算,注意,水壶倒的次数比子段长度小1(自己想想为什么)
if (k > max)
max = k; // 判断大小
}
printf("%d", max); // 输出
return 0;
}
P8772 [蓝桥杯 2022 省 A] 求和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
AC Code:
注意开long long!
#include
using namespace std;
const int N = 2e5 + 5;
int a[N], n;
long long sum[N], ans;
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
for (int i = 1; i < n; i++)
{
ans += a[i] * (sum[n] - sum[i]);
}
cout << ans;
return 0;
}
P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
方法一:dp
AC Code:
//方法1:dp
#include
#include
using namespace std;
const int N=2e5+5;
int a[N],n,dp[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin >> n;
for(int i=1;i<=n;i++)
{
cin >> a[i];
}
dp[1]=a[1];
for(int i=2;i<=n;i++)
{
dp[i]=max(dp[i-1]+a[i],a[i]);
}
int maxx=-10000;
for(int i=1;i<=n;i++)
{
if(dp[i]>maxx)
maxx=dp[i];
}
cout << maxx;
return 0;
}
方法二:双指针
AC Code:
#include
#include
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
int a, n, s = 0, ans = -2e9;
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a;
if (s + a > a)
s += a;
else
s = a;
ans = max(ans, s);
}
cout << ans;
return 0;
}
P2367 语文成绩 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
知识点:一维差分与一维前缀和
AC Code:
#include
#include
using namespace std;
const int N = 5e6 + 5;
int a[N], d[N], n, p;
inline int read()
{
bool sym = 0;int res = 0;char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
int main()
{
n = read(), p = read();
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
d[i] = a[i] - a[i - 1];
int x, y, z;
while (p--)
{
x = read(), y = read(), z = read();
d[x] += z, d[y + 1] -= z;
}
for (int i = 1; i <= n; i++)
d[i] += d[i - 1];
int minn = 1000;
for (int i = 1; i <= n; i++)
if (d[i] < minn)
minn = d[i];
printf("%d", minn);
return 0;
}
P3397 地毯 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
知识点:二维前缀和与二维差分
AC Code:
#include
using namespace std;
const int N = 1e3 + 5;
int n, m, map[N][N],sum[N][N],x1, y1, x2, y2;
inline int read()
{
bool sym = 0;int res = 0;char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
int main()
{
n = read(), m = read();
while (m--)
{
x1 = read(), y1 = read(), x2 = read(), y2 = read();
map[x1][y1]++;
map[x2+1][y1]--;
map[x1][y2+1]--;
map[x2+1][y2+1]++;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
sum[i][j]=map[i][j]+sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (j != n)
printf("%d ", sum[i][j]);
else
printf("%d\n", sum[i][j]);
}
}
return 0;
}
A - 子段求和
2021级训练赛20211004 - Virtual Judge (vjudge.net)
注意开long long !
AC Code:
#include
using namespace std;
const int N = 5e4 + 5;
long long sum[N];
int n, q, l, r, a[N];
inline int read()
{
bool sym = 0;int res = 0;char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
int main()
{
n = read();
for (int i = 1; i <= n; i++)
a[i] = read();
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + a[i];
q = read();
while (q--)
{
l = read(), r = read();
printf("%lld\n", sum[r + l - 1] - sum[l - 1]);
}
return 0;
}
F - 前缀异或
2021级训练赛20211004 - Virtual Judge (vjudge.net)
注意把最后的减法运算改为异或运算!
AC Code:
#include
using namespace std;
const int N=1e5+5;
int a[N],n,m,l,r,prexor[N];
inline int read()
{
bool sym = 0;int res = 0;char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
a[i]=read();
}
prexor[1]=a[1];
for(int i=2;i<=n;i++)
{
prexor[i]=prexor[i-1]^a[i];
}
m=read();
while(m--)
{
l=read(),r=read();
printf("%d\n",prexor[r]^prexor[l-1]);
}
return 0;
}
G - Color the ball
2021级训练赛20211004 - Virtual Judge (vjudge.net)
需要注意的点:
1.每次while循环,首先要把差分数组清零,不然会因为上一轮的操作改变数组d的值.
2.注意找n的替身!
AC Code:
#include
#include
using namespace std;
const int N=1e5+5;
int n,d[N],a,b;
inline int read()
{
bool sym = 0;int res = 0;char ch = getchar();
while (!isdigit(ch)) sym |= (ch == '-'), ch = getchar();
while (isdigit(ch)) res = (res << 3) + (res << 1) + (ch ^ 48), ch = getchar();
return sym ? -res : res;
}
int main()
{
while (cin >> n, n != 0)
{
memset(d, 0, sizeof d); // 清零
int x = n; // 替身
while (n--)
{
a = read(), b = read();
d[a]++, d[b + 1]--;
}
for (int i = 1; i <= x; i++)
{
d[i] += d[i - 1];
}
for (int i = 1; i <= x; i++)
{
if (i != x)
printf("%d ", d[i]);
else
printf("%d\n", d[i]);
}
}
return 0;
}
I - Monitor
前缀和差分练习 - Virtual Judge (vjudge.net)
AC Code:
#include
#include
using namespace std;
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
int monitor, thieves;
scanf("%d", &monitor);
int ldx, ldy, rux, ruy;
vector> record(n + 2, vector(m + 2)), glass(n + 2, vector(m + 2));
while (monitor--)
{
// 左上角x,y 右下角x,y
scanf("%d%d%d%d", &ldx, &ldy, &rux, &ruy);
++record[ldx][ldy];
++record[rux + 1][ruy + 1];
--record[ldx][ruy + 1];
--record[rux + 1][ldy];
}
for (int row = 1; row <= n; ++row)
{
for (int col = 1; col <= m; ++col)
record[row][col] += record[row - 1][col] + record[row][col - 1] - record[row - 1][col - 1];
}
for (int row = 1; row <= n; ++row)
{
for (int col = 1; col <= m; ++col)
glass[row][col] += glass[row - 1][col] + glass[row][col - 1] - glass[row - 1][col - 1] + (record[row][col] > 0 ? 1 : 0);
}
scanf("%d", &thieves);
while (thieves--)
{
scanf("%d%d%d%d", &ldx, &ldy, &rux, &ruy);
int area = glass[rux][ruy] - glass[rux][ldy - 1] - glass[ldx - 1][ruy] + glass[ldx - 1][ldy - 1];
if (area == (rux - ldx + 1) * (ruy - ldy + 1))
printf("YES\n");
else
printf("NO\n");
}
}
return 0;
}
J - Boxes
前缀和差分练习 - Virtual Judge (vjudge.net)
AC Code:
#include
#include
#include
typedef long long ll;
using namespace std;
ll n;
ll a[100010];
ll dis[100010];
int main()
{
ll sum=0;
scanf("%lld",&n);
for(int i=0;i
B - Average Superhero Gang Power
前缀和差分 - Virtual Judge (vjudge.net)
题目大意:对于一群超级英雄,他们分别有最初的力量值,有两种操作:1、给任意英雄力量加一,2、去掉任意一个英雄。求操作后英雄们平均力量的最大值。共有n个超级英雄,每个人最多操作k次(即力量最多加k),总可操作次数为m。
思路:先初始化总力量sum和平均力量ans=(sum+min(k*n,m))/n; 因为n个人每人操作k次的总数有可能超过m,所以是min(k*n,m),然后力量从小到大排序,每次减掉一个人,依次比较上次的ans和新的ans2,取大值。对了还要注意数据范围。
AC Code:
#include
#include
using namespace std;
typedef long long ll;
const int maxn = 100010;
double a[maxn];
int main()
{
ll n, k, m;
double sum = 0;
cin >> n >> k >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
sum += a[i];
}
sort(a + 1, a + n + 1);
double ans = (sum + min(k * n, m)) / n;
for (int i = 0; i < n && i <= m; i++)
{
sum -= a[i];
double ans2 = (sum + min(k * (n - i), m - i)) / (n - i);
ans = max(ans, ans2);
}
printf("%.20f\n", ans);
}
H - Molly's Chemicals
前缀和差分 - Virtual Judge (vjudge.net)
这个题目是思维题,我开始也想直接暴力,就是直接枚举k的次方,直到它到达10的14次方,
这个不是很大,如果是2都只要几十次,所以肯定不会超时,不过不知道怎么操作。
看了题解,觉得map真是一个好东西,它可以存很大的数,耗费的空间也不是很大。
我们就可以把数值存到map里面。
具体就是,先处理这些数,求出前缀和,然后就是求出k的幂。
最后枚举。
AC Code:
#include
#include
#include
#include