day1 前缀和、差分、二分答案
这里贴个个人觉得很舒服的二分模板:
double l = 0.0, r = 1.0, mid; while (r - l > EPS) { mid = (l + r) / 2; if (/*对应条件*/ > 0) l = mid; else r = mid; }
day2 倍增、st表、快速幂
快速幂和取模的模板
LL qpow(LL a, LL b) { LL base = a; LL ans = 1; while (b) { if (b & 1) ans *= base; b >>= 1; base *= base; } return ans; } //取模 LL qpow(LL a, LL b) { LL base = a; LL ans = 1; while (b) { if (b & 1) { ans *= base; ans %= MOD; } b >>= 1; base *= base; base %= MOD; } return ans % MOD; }
ST表的模板:
void ST() { for (int i = 1; i <= n; i++) f[i][0] = a[i]; int t = log(n) / log(2) + 1; for (int j = 1; j < t; ++j) { for (int i = 1; i <= n - (1 << j) + 1; ++i) { f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); } } } int stQuery(int l, int r) { int k = log(r - l + 1) / log(2); return max(f[l][k], f[r - (1 << k) + 1][k]); }
速度是O(1),用于处理区间内最大值/最小值查询的问题
如果是最小值,把板子里的max相应换成min即可
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
day3 并查集进阶
放一个带权值的题解吧
#include<iostream> #include<cstdio> using namespace std; struct food{ int fat;//祖先 int ran;//与fat的关系 }ani[150050]; int find(int x){ if(x==ani[x].fat) return ani[x].fat; int y=find(ani[x].fat); ani[x].ran=(ani[x].ran+ani[ani[x].fat].ran)%3;// ani[x].fat=y; return y; } int uni(int typ,int x,int y){ int x1=find(x); int y1=find(y); if(x1==y1){ if((ani[x].ran-ani[y].ran+3)%3==typ-1) return 0; else return 1; } ani[x1].fat=y1; ani[x1].ran=(-ani[x].ran+typ-1+ani[y].ran+3)%3; return 0; } int main(){ int n,k,ans=0; int typ,smt1,smt2; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++){ ani[i].fat=i; ani[i].ran=0; } while(k--){ scanf("%d%d%d",&typ,&smt1,&smt2); if(smt1==smt2&&typ==2) ans++; else if(smt1>n||smt2>n) ans++; else ans+=uni(typ,smt1,smt2); } printf("%d\n",ans); return 0; }
DAY4、5 线段树&树状数组
#include<cstdio> #include<cmath> #include<algorithm> #include<iostream> using namespace std; long long int sum[2000200]; long long int lazy[2000200]; void build(int l,int r,int rt) { lazy[rt]=0; if(l==r){ scanf("%lld",&sum[rt]); return; } int mid=(l+r)/2; build(l,mid,rt<<1); build(mid+1,r,rt<<1|1); sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); } void pushdown(int l,int r,int rt){ if(lazy[rt]){ lazy[rt<<1]+=lazy[rt]; lazy[rt<<1|1]+=lazy[rt]; int mid=(l+r)/2; sum[rt<<1]+=lazy[rt]; sum[rt<<1|1]+=lazy[rt]; lazy[rt]=0; } } void change(int l,int r,int rt,int L,int R,int c){ //c需要添加的值 if(l>=L&r<=R){ lazy[rt]+=c; sum[rt]+=c; return; } pushdown(l,r,rt); int mid=(l+r)/2; if(R>mid) change(mid+1,r,rt<<1|1,L,R,c); if(L<=mid) change(l,mid,rt<<1,L,R,c); sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); } long long query(int l,int r,int rt,int a,int b){ //l,r:查询区间 //a,b:结点区间 //rt:节点下标 if(l>=a&&r<=b) return sum[rt]; pushdown(l,r,rt); int mid=(l+r)/2; long long ans=-65535; if(a<=mid) ans=max(ans,query(l,mid,rt<<1,a,b)); if(b>mid) ans=max(ans,query(mid+1,r,rt<<1|1,a,b)); sum[rt]=max(sum[rt<<1],sum[rt<<1|1]); return ans; } int main(){ int t,n,k; while(~scanf("%d%d",&n,&t)){ build(1,n,1); char s[2]; while(t--){ int a,b,c; scanf("%s",&s); if(s[0]=='U'){ scanf("%d%d",&a,&b); int temp=query(1,n,1,a,a); change(1,n,1,a,a,b-temp); } else{ scanf("%d%d",&a,&b); printf("%lld\n",query(1,n,1,a,b)); } } } return 0; }
一般来说如果要修改某一个范围的值采用线段树,(范围更新)
单点更新的时候树状数组就行:
#include<bits/stdc++.h> using namespace std; const int M =100000+100; int n,t; int a[M],C[M]; int lowbit(int x) { return x&(-x); } int sum(int x) { int ret=0; while(x) { ret+=C[x]; x-=lowbit(x); } return ret; } void updata(int x,int d) { while(x<=n) { C[x]+=d; x+=lowbit(x); } } int main() { scanf("%d",&t); for(int i=1;i<=t;i++) { printf("Case %d:\n",i); scanf("%d",&n); for(int i=1;i<=n;i++) C[i]=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); updata(i,a[i]); } char s[10]; while(scanf("%s",s)&&s[0]!='E') { int q,w; scanf("%d%d",&q,&w); if(s[0]=='A') updata(q,w); else if(s[0]=='S') updata(q,-w); else printf("%d\n",sum(w)-sum(q-1)); } } return 0; }
实际上day5还有不少鬼畜进阶内容……临阵磨枪咱们就先忽略了吧真考那些还有一大批神仙能做出来那我甘愿劝退
Day 6 单调栈 单调队列
滑动窗口:
#include<iostream> #include<cstdio> #define Max 1000001 using namespace std; int m; int a[Max],q[Max],ans_min[Max],ans_max[Max],h,t; int main(){ int n; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); h=1,t=0; for(int i=1;i<=n;i++){ while(h<=t&&a[q[t]]>=a[i]) t--; q[++t]=i; while(i-m>=q[h]) h++; ans_min[i]=a[q[h]]; if(i>=m){ printf("%d",a[q[h]]); if(i!=n) printf(" "); else printf("\n"); } } h=1,t=0; for(int i=1;i<=n;++i){ while(h<=t&&a[q[t]]<=a[i]) t--; q[++t]=i; while(i-m>=q[h]) h++; ans_max[i]=a[q[h]]; if(i>=m){ printf("%d",a[q[h]]); if(i!=n) printf(" "); else printf("\n"); } } return 0; }