算法培训大纲

离散化#

  • 将无穷大集合的若干元素映射到有限集合便于统计的方法
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 stdconst 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;
}

引用#

线段树
动态开点线段树

posted @   lxp_blog  阅读(133)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示
主题色彩