算法培训大纲
离散化#
- 将无穷大集合的若干元素映射到有限集合便于统计的方法
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r)
{
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; // 映射到1, 2, ...n
}
也可以利用lower_bound查找
a=lower_bound(alls.begin(),alls.end(),a)-alls.begin();
unique
for(int i=1;i<=n;i++){
if(i==1||a[i]!=a[i-1])
b[++m]=a[i];
}
#include<bits/stdc++.h>
using namespace std;
const int N=1000100;
#define maxn 1000000000
int n;
typedef long long LL;
vector<LL > alls;
LL a[N];
LL find(LL x)
{
int l=0,r=alls.size()-1;
while(l<r)
{
int mid=(l+r)>>1;
if(alls[mid]>=x)r=mid;
else l=mid+1;
}
return l;
}
LL p[N];
int main()
{
cin>>n;
LL res=0;
a[0]=0;
alls.push_back(0);
for(int i=1;i<=n;i++)
{ LL x;
char s[5];
scanf("%lld%s",&x,s);
if(s[0]=='L')x*=-1;
res+=x;
a[i]=res;
alls.push_back(res);
alls.push_back(res+1);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
for(int i=0;i<n;i++)
{
int l =find(a[i]),r=find(a[i+1]);
if(l>r)swap(l,r);
p[l]++,p[r+1]--;
}
for(int i=0;i<alls.size();i++)
{
if(i)p[i]+=p[i-1];
}
res=0;
for(int i=0;i<alls.size();)
{
int j=i;
while(j<alls.size()&&p[j]>=2)
{
j++;
}
if(i==j)i++;
else
{
// cout<<alls[j-1]<<" "<<alls[i]<<endl;
res+=alls[j-1]-alls[i];
i=j;
}
}
cout<<res<<endl;
return 0;
}
并查集#
- 维护集合
- 边带权/扩展域
扩展域并查集
#include<bits/stdc++.h>
using namespace std;
const int N = 10010;
int n,m;
vector<int> alls;
struct edge{
int l,r,t;
}q[N];
int f[N<<1];
int find(int x){
if(x!=f[x])f[x]=find(f[x]);
return f[x];
}
int main()
{
cin>>n>>m;
for(int i=0;i<N*2;i++)f[i]=i;
for(int i=0;i<m;i++){
int a,b;string c;
cin>>a>>b>>c;
if(c[0]=='o'){
q[i]={a,b,1};
}
else q[i]={a,b,0};
alls.push_back(a-1);
alls.push_back(b);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
for(int i=0;i<m;i++){
auto& [a,b,c]=q[i];
a=lower_bound(alls.begin(),alls.end(),a-1)-alls.begin();
b=lower_bound(alls.begin(),alls.end(),b)-alls.begin();
int a_even=find(a+N),a_odd=find(a);
int b_even=find(b+N),b_odd=find(b);
if(c==0){ //agreement even
if(a_odd==b_even) {
cout<<i<<endl;
return 0;
}
f[a_odd]=b_odd;
f[a_even]=b_even;
}
else{
if(a_odd==b_odd){
cout<<i<<endl;
return 0;
}
f[a_odd]=b_even;
f[a_even]=b_odd;
}
}
cout<<m<<endl;
return 0;
}
树状数组#
- 单点增加
- 查询前缀和
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2000010;
int t[N];
int n;
typedef long long LL;
int a[N];
int lmax[N];
int lmin[N];
int lowbit(int x)
{
return x & -x;
}
//将序列中第x个数加上k
void add(int x, int k)
{
for(int i = x; i <= n; i += lowbit(i)) t[i] += k;
}
//查询序列前x个数的和
int find(int x)
{
int sum = 0;
for(int i = x; i; i -= lowbit(i)) sum += t[i];
return sum;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
{
int y=a[i];
lmax[i]=find(n)-find(y);
lmin[i]=find(y-1);
add(y,1);
}
memset(t,0,sizeof t);
LL A,V;
A=V=0;
for(int i=n;i>=1;i--)
{
int y=a[i];
V+= (LL)lmax[i]*(find(n)-find(y));
A+=(LL)lmin[i]*(find(y-1));
add(y,1);
}
cout<<V<<" "<<A<<endl;
}
离散化+树状数组#
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int n,l,r;
int a[N];
typedef long long LL;
LL s[N];
vector<LL> alls;
int lower(LL x){
return lower_bound(alls.begin(),alls.end(),x)-alls.begin();
}
int tr[N*3];
int lowbit(int x){
return x&-x;
}
void add(int k,int v){
for(int i=k;i<alls.size();i+=lowbit(i)){
tr[i]+=v;
}
}
int find(int k)
{
int res =0;
for(int i=k;i;i-=lowbit(i)){
res+=tr[i];
}
return res;
}
int main(){
cin>>n>>l>>r;
// l<=x<=r;
for(int i=1;i<=n;i++){
cin>>a[i];
s[i]=s[i-1]+a[i];
alls.push_back(s[i]);
alls.push_back(s[i]-l);
alls.push_back(s[i]-r-1);
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
add(lower(s[0]),1);
LL ans=0;
for(int i=1;i<=n;i++){
LL x=s[i]-r-1,y=s[i]-l;
ans+=find(lower(y))-find(lower(x));
add(lower(s[i]),1);
}
cout<<ans<<endl;
return 0;
}
线段树#
- 动态维护区间
动态开点线段树#
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 3.4e6 + 5;
const LL MAXN = 1e10;
struct SegmentTree {
int lc,rc,dat;
}tr[4 * N];
int n,root,tot = 0,l,r;
long long s[N],res = 0;
int build() {
tot++;
tr[tot].lc = tr[tot].rc = tr[tot].dat = 0;
return tot;
}
void insert(int p,LL val,int add,LL L = -MAXN,LL R = MAXN) {
if(L == R) {
tr[p].dat += add;
return;
}
LL mid = (L + R) >> 1;
if(val <= mid) {
if(!tr[p].lc) tr[p].lc = build();
insert(tr[p].lc,val,add,L,mid);
}
else {
if(!tr[p].rc) tr[p].rc = build();
insert(tr[p].rc,val,add,mid + 1,R);
}
tr[p].dat = tr[tr[p].lc].dat + tr[tr[p].rc].dat;
}
int query(int p,LL lf,LL rg,LL L = -MAXN,LL R = MAXN) {
if(lf <= L && R <= rg) return tr[p].dat;
LL mid = (L + R) >> 1; int ans = 0;
if(lf <= mid) {
if(!tr[p].lc) tr[p].lc = build();
ans += query(tr[p].lc,lf,rg,L,mid);
}
if(rg > mid) {
if(!tr[p].rc) tr[p].rc = build();
ans += query(tr[p].rc,lf,rg,mid + 1,R);
}
return ans;
}
int main() {
cin>>n>>l>>r;
for(int i = 1,x; i <= n; i++) {
cin>>x;
s[i] = s[i - 1] + x;
}
root = build();
insert(root,s[0],1);
for(int i = 1; i <= n; i++) {
res += query(root,s[i] - r,s[i] - l);
insert(root,s[i],1);
}
printf("%lld",res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)