首页 > 其他分享 >Living-Dream 系列笔记 第12期

Living-Dream 系列笔记 第12期

时间:2024-03-09 12:36:10浏览次数:20  
标签:Living 12 前缀 int sum long le ans Dream

本期主要讲解一维前缀和技巧。

知识点

我们令 \(a_i\) 表示原数组的第 \(i\) 个元素,则 \(sum_i\) 表示 \(a_i\) 前 \(i\) 个元素之和,即:

\[sum_i=\sum^{i}_{j=1} a_j \]

我们知道,\(a\) 数组前 \(i\) 个元素的和 \(=\) 前 \(i-1\) 个元素的和 \(+ a_i\)。于是便可得到 \(sum\) 数组的递推式:

\[sum_i=sum_{i-1}+a_i \]

特别的:

\[sum_0=0,sum_1=a_1 \]

前缀和技巧的使用场景一般为反复多次静态区间的元素和。

若指定一个静态区间 \(a\) 的左端点 \(L\) 和右端点 \(R\),求该区间的元素和,则有公式:

\[\sum_{L \le i \le R} a_i = sum_R-sum_{L-1} \]

例题

T1

模板题,不讲。

T2

挺有意思的一道题。其实很简单。

我们知道传送器仅可使用一次,所以我们选择序列中元素和最大的长度为 \(k\) 的区间的左端点处使用传送器即可。

因此维护一个前缀和,\(O(n)\) 地枚举起点 / 终点,利用前缀和算出时间,最后答案就是总时间 \(-\) 最大时间。

注意开 long long

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,k,ans=-1e18;
int a[1000031],sum[1000031];

signed main(){
	cin>>n>>k;
	for(int i=1;i<=n-1;i++) cin>>a[i],sum[i]=sum[i-1]+a[i];
	if(k==0){
		cout<<sum[n-1]; return 0;
	}
	for(int i=1;i+k-1<=n-1;i++) ans=max(ans,sum[i+k-1]-sum[i-1]);
	cout<<sum[n-1]-ans;
	return 0;
}

T3

也挺有意思的一道题。

很容易想到枚举左右端点 \(l,r\),对于所有满足 \(\sum_{l \le i \le r} a_i \bmod 7 = 0\) 的区间长度取 \(\max\) 即为答案。时间复杂度 \(O(n^2)\),原地爆炸。

我们知道 \(\sum_{l \le i \le r} a_i = sum_r - sum_{l-1}\),若这个式子 \(\bmod \ 7=0\),则说明 \(sum_r \equiv sum_{l-1} \pmod 7\)。

而一个自然数 \(\bmod \ 7\) 的余数仅可能为 \(0 \sim 6\) 共 \(7\) 种可能,因此我们考虑枚举这 \(7\) 种可能,对于每一种可能将 \(a\) 数组分别正序和倒序扫描一遍,求出 \(\bmod \ 7\) 余数为当前余数的第一个 \(sum_i\) 和最后一个 \(sum_j\),区间 \([i+1,j]\) 便是对于当前余数的最大区间,对所有这样的区间长度取 \(\max\) 即可。

为避免溢出,需要在计算 \(sum\) 数组时先模上 \(7\)。

于是你写完了。

然后被 hack 了。

我们发现当余数为 \(0\) 时,\(l\) 最小可能为 \(0\),因此将内循环中枚举 \(j\) 的初值设为 \(0\) 即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1;
int a[500031],sum[500031];

signed main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i],sum[i]=(sum[i-1]+a[i])%7;
    //for(int i=1;i<=n;i++) cout<<sum[i]<<' ';
    //cout<<'\n';
    for(int i=0;i<7;i++){
        int st,ed;
        for(int j=0;j<=n;j++){ if(sum[j]==i){ st=j; break; } }
        for(int j=n;j>=0;j--){ if(sum[j]==i){ ed=j; break; } }
        ans=max(ans,ed-st);
    }
    cout<<ans;
    return 0;
}

习题

T4

题目就是说要你改变最少的字符来实现字符串产生一边 \(0\) 一边 \(1\) 或者一边 \(1\) 一边 \(0\) 的局面。

要产生上述局面,则 \(0\) 和 \(1\) 的连续子串会有一个分界点,我们可以考虑枚举这个分界点。

同时,我们维护两个前缀和数组 \(z\) 和 \(o\),其中 \(z_i\) 和 \(o_i\) 分别表示前 \(i\) 的字符的 \(0\) 的个数和 \(1\) 的个数。

于是,对于一个分界点,利用前缀和数组快速求出前 \(i\) 个字符的 \(0\) 个数和后面字符的 \(1\) 个数,从而计算出还需要改变多少字符才能达成前面 \(1\) 后面 \(0\) 的局面,反之同理。

对于达成两种局面所需要改变的字符数取 \(\min\) 即可。

多测清空!多测清空!多测清空!

#include<bits/stdc++.h>
using namespace std;

int t,ans;
int sz,so,z[1031],o[1031];
string s;

int main(){
    cin>>t;
    while(t--){
        sz=0,so=0;
        memset(z,0,sizeof(z)),memset(o,0,sizeof(o));
        cin>>s;
        for(int i=0;s[i];i++){
            z[i]=z[i-1],o[i]=o[i-1];
            if(s[i]=='0') sz++,z[i]++;
            else so++,o[i]++;
        }
        ans=1e9;
        for(int i=0;s[i];i++){
            ans=min(ans,z[i]+so-o[i]);
            ans=min(ans,o[i]+sz-z[i]);
        }
        cout<<ans<<'\n';
    }
    return 0;
}

T5

求出 \(0 \sim 10^4\) 的是质数的 \(f(i)\),并累加入前缀和数组 \(q\) 中。

对于每次询问,输出 \(\dfrac{q_r-q_{l-1}}{b-a+1}+10^{-6}\) 即可,加上 \(10^{-6}\) 是因为题目卡精度。

#include<bits/stdc++.h>
using namespace std;

int l,r,n;
double ans,q[10031];

int f(int x){ return x*x+x+41; }
bool isp(int x){
    if(x<2) return 0;
    for(int i=2;i*i<=x;i++) if(x%i==0) return 0;
    return 1;
}
void init(){
    q[0]=1;
    for(int i=1;i<=10000;i++) q[i]=q[i-1]+isp(f(i));
}

int main(){
    init();
    while(cin>>l>>r){
        n=r-l+1,ans=q[r]-q[l-1];
        cout<<setprecision(2)<<fixed<<ans*100.0/(double)n+1e-6<<'\n';
    }
    return 0;
}

标签:Living,12,前缀,int,sum,long,le,ans,Dream
From: https://www.cnblogs.com/XOF-0-0/p/18062514

相关文章

  • Living-Dream 系列笔记 第13期
    本期主要讲解二维前缀和。知识点我们令\(a_{i,j}\)表示原数组,则\(sum_{i,j}\)为\(a\)的二维前缀和数组。根据容斥原理,得到递推式:\[sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+a_{i,j}\]二维前缀和适用于求静态矩阵的子矩阵元素和。若我需要求一个左上角坐标为......
  • Living-Dream 系列笔记 第10期
    本期主要讲解进阶\(\text{DFS}\)。知识点\(\text{DFS}\)求解连通块问题:定义:若一个点集中的所有点都能互达,且与集合外的点无法互达,则称此点集为一个连通块。考查方式:求连通块数量/大小/周长。例题T1在\(\text{DFS}\)函数中传入参数\(x\)和\(str\),分别表示......
  • Living-Dream 系列笔记 第8期
    本期主要讲解的与上期相同。例题T1上课的时候调这个题感觉要吐了\(qwq\)。。。首先读入\(n\)行字符串,可以采取忽略中间无关单词的方式来直接读取\(X\)和\(Y\)。将所有名字存入\(a\)数组,对\(a\)数组按字典序排序后就可以开始\(\text{DFS}\)了,这里建议使用next_pr......
  • Living-Dream 系列笔记 第9期
    模拟赛掉大分(悲T1板子,不讲。T2首先很明显这题是个去重全排列。和模板的区别就是加了一个\(sum\)参数记录目前已经放了几个苹果。当\(x=n+1\)时若\(sum=m\),则更新答案。同时加入一个剪枝:若在搜索过程中\(sum>m\),则直接return结束搜索。#include<bits/stdc++.h>usi......
  • Living-Dream 系列笔记 第6期
    模拟赛。寄。T1对于每次询问,二分查找数组中对应值的原下标即可,因此需要用结构体存储原始数据和原始下标。这当然是比较麻烦的做法。另一种做法则是开一个map替代桶来存储数组中每个元素的下标,对于每个询问输出即可。另外值得注意的是,本题默认询问之间相互独立。时间复杂度......
  • Living-Dream 系列笔记 第7期
    本期主要讲解深度优先搜索\(\text{DFS}\)。知识点种类:全排列。可以想象为填格子。去重全排列,即组合。时间复杂度均为\(O(n!)\)。\(\text{DFS}\)题的特征:求方案总数/最值。数据范围极小(一般\(n\le20\))。无法直接暴力枚举(因为循环嵌套层数不确定)。......
  • Living-Dream 系列笔记 第4期
    本期主要讲解二分答案。知识点使用场景:最小值最大化,或最大值最小化。在限制条件下找最值。与二分查找的区别:L、R均为答案,而非下标。输出:最大化输出L,反之输出R。例题T1二分\(M\)的值,边界为\(L=-1,R=\max{\{a_i\}}\)。每次枚举到一个\(mid\)就对于每个......
  • Living-Dream 系列笔记 第5期
    本期主要讲解二分答案的进阶。例题T1二分需要的秒数,在check函数中对于每件衣服,若其在\(x\)秒内无法自然晒干,则使用烘干机,并令\(sum\)加上使用烘干机的秒数,最后判断\(sum\)是否\(\lex\)即可。\(Trick\):二分边界需要按数据范围尽可能开大,不能开小了。#include<bits/......
  • Java蓝桥杯题目——1264排个序
    题目 思路:1、输入数据2、用冒泡排序将数组(下标为pj的)部分升序,3、判断是否有前一个元素大于后一个元素(降序),有则返回false注意:(1)数组p元素的取值不能大于数组a的长度,因为p元素是a的下标(2)数组下标越界问题,使用i<a.length判断(3)并非所有元素都要降序才返回false,只要有前一个元......
  • CF1218A
    虚高*2800。放模拟赛T2人均切了。先想树的情况怎么做。枚举每个起点,剩下的贡献就是定值。求这个值可以钦定\(1\)为根求出所有的\(siz\),然后枚举\(i\)为起点,以\(i\)为起点的答案就是\(\sumsiz_i\)加上\(i\)到\(1\)路径上,不含\(1\)的所有点的\(\sum_jn-2\time......