ACM算法模版
数据结构
树状数组
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+100,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m;
int a[N];
int c[N];
int lowbit(int x)
{
return x&(-x);
}
int query(int x)
{
int s=0;
for(;x>0;x-=lowbit(x))
{
s+=c[x];
}
return s;
}
void modify(int id,int x)
{
for(;id<=n;id+=lowbit(id))
{
c[id]+=x;
}
}
void solve()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i],modify(i,a[i]);
while(m--)
{
int id,x,y;
cin>>id>>x>>y;
if(id==1) modify(x,y);
else cout<<query(y)-query(x-1)<<endl;
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
线段树
线段树单点修改
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=200010;
typedef long long ll;
struct node
{
int minx;
int l,r;
}tr[N*4];
int a[N];
void update(int p)
{
tr[p].minx=min(tr[2*p].minx,tr[2*p+1].minx);
}
void build(int p,int l,int r)
{
if(l==r){
tr[p].minx=a[l];
return ;
}else{
int mid=(l + r) / 2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
}
update(p);
}
// 当前节点为p,[l,r]为当前区间,修改a[pos]->val
void change(int p,int l,int r,int pos,int val)
{
if(l==r){
tr[p].minx=val;
//a[pos]=val;
}else{
int mid=(l+r)/2;
if(pos<=mid) change(2*p,l,mid,pos,val);
else change(2*p+1,mid+1,r,pos,val);
}
//重要!!
update(p);
}
//[ql,qr]为要查询的区间
int query(int p,int l,int r,int ql,int qr) //时间复杂度O(logn)
{
if(ql==l&&qr==r){
return tr[p].minx;
}else{
int mid=(l + r) / 2;
//[l,mid],[mid+1,r]
if(qr<=mid) return query(2*p,l,mid,ql,qr);
else if(ql>mid) return query(2*p+1,mid+1,r,ql,qr);
else{
//ql<=mid,qr>mid
// [ql,mid],[mid+1,qr]
return minx(query(2*p,l,mid,ql,mid),
query(2*p+1,mid+1,r,mid+1,qr));
}
}
}
线段树区间修改
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010;
int n,m;
struct node
{
int sum,t;
int l,r;
}tr[N*4];
int a[N];
void settag(int p,int tag)
{
tr[p].sum+=tag*(tr[p].r-tr[p].l+1);
tr[p].t+=tag;
}
void pushdown(int p)
{
if(tr[p].t)
{
tr[2*p].sum+=tr[p].t*(tr[2*p].r-tr[2*p].l+1);
tr[2*p].t+=tr[p].t;
tr[2*p+1].sum+=tr[p].t*(tr[2*p+1].r-tr[2*p+1].l+1);
tr[2*p+1].t+=tr[p].t;
tr[p].t=0;
}
}
void update(int p)
{
tr[p].sum=(tr[2*p].sum+tr[2*p+1].sum);
}
void build(int p,int l,int r)
{
tr[p].l=l;tr[p].r=r;tr[p].t=0;
if(l==r){
tr[p].sum=a[l];
}else{
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
update(p);
}
}
void modify(int p,int l,int r,int ql,int qr,int tag)
{
if(ql==l&&qr==r)
{
settag(p,tag);
return ;
}
pushdown(p);
int mid=(l+r)/2;
if(qr<=mid) modify(2*p,l,mid,ql,qr,tag);
else if(ql>mid) modify(2*p+1,mid+1,r,ql,qr,tag);
else{
modify(2*p,l,mid,ql,mid,tag);
modify(2*p+1,mid+1,r,mid+1,qr,tag);
}
update(p);
}
int query(int p,int l,int r,int ql,int qr)
{
if(ql==l&&qr==r)
{
return tr[p].sum;
}
pushdown(p);
int mid=(l+r)/2;
if(qr<=mid) return query(2*p,l,mid,ql,qr);
else if(ql>mid) return query(2*p+1,mid+1,r,ql,qr);
else {
return query(2*p,l,mid,ql,mid)+
query(2*p+1,mid+1,r,mid+1,qr);
}
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--)
{
char op;
cin>>op;
if(op=='Q'){
int l,r;
cin>>l>>r;
int s=query(1,1,n,l,r);
cout<<s<<endl;
}else{
int l,r,d;
cin>>l>>r>>d;
modify(1,1,n,l,r,d);
}
}
return 0;
}
线段树上二分
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
int a[N];
struct node
{
int val;
}tr[N*4];
void update(int p)
{
tr[p].val=max(tr[2*p].val,tr[2*p+1].val);
}
void build(int p,int l,int r)
{
if(l==r) {
tr[p].val=a[l];
return ;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
update(p);
}
void modify(int p,int l,int r,int pos,int d)
{
if(l==r)
{
tr[p].val=d;
a[pos]=d;
return ;
}
int mid=(l+r)/2;
if(pos<=mid) modify(2*p,l,mid,pos,d);
else modify(2*p+1,mid+1,r,pos,d);
update(p);
}
int search(int p,int l,int r,int ql,int qr,int d)
{
if(l==ql&&r==qr)
{
if(tr[p].val<d) return -1;
else
{
if(l==r) return l;
int mid=(l+r)/2;
if(tr[2*p].val>=d) return search(2*p,l,mid,ql,qr,d);
else return search(2*p+1,mid+1,r,ql,qr,d);
}
}
int mid=(l+r)/2;
if(qr<=mid) return search(p*2,l,mid,ql,qr,d);
else if(ql>mid) return search(2*p+1,mid+1,r,ql,qr,d);
else{
int res=search(2*p,l,mid,ql,mid,d);
if(res==-1) return search(2*p+1,mid+1,r,mid+1,qr,d);
else return -1;
}
}
void solve()
{
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
cin>>T;
while(T--)
{
solve();
}
return 0;
}
动态开点线段树
主席树——可持久化权值线段树
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=2e5+3;
using i64 = long long;
int n,m,q;
struct node
{
int val,ls,rs,sum;
}tr[2*N+N*18];
int a[N];
vector<int> vy;
int rt[N],tot;
int getid(int x) // 离散化
{
return lower_bound(vy.begin(),vy.end(),x)-vy.begin()+1;
}
int build(int l,int r) //建树,最初只有2*n-1个节点
{
int p=++tot;
if(l==r) return p;
int mid=(l+r)/2;
tr[p].ls = build(l,mid);
tr[p].rs = build(mid+1,r);
return p;
}
// pos为要插入的位置,[l,r]为操作的范围,f为该节点的历史节点
int update(int pos,int l,int r,int f)
{
int p=++tot;
tr[p].ls=tr[f].ls,tr[p].rs=tr[f].rs,tr[p].sum=tr[f].sum+1;
if(l==r) return p;
int mid=(l+r)/2;
if(pos<=mid) tr[p].ls=update(pos,l,mid,tr[p].ls);
else tr[p].rs=update(pos,mid+1,r,tr[p].rs);
return p;
}
//查询区间[l,r]的第k小数,u为前一次版本的根节点,v为当前版本的根节点
int query(int u,int v,int l,int r,int k)
{
int mid=(l+r)/2;
int x=tr[tr[v].ls].sum-tr[tr[u].ls].sum; //左子树的新增个数
if(l==r) return l;
if(k<=x) return query(tr[u].ls,tr[v].ls,l,mid,k); //说明左子树新增个数大于k,一定在左子树
else return query(tr[u].rs,tr[v].rs,mid+1,r,k-x);
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
vy.push_back(a[i]);
}
sort(vy.begin(),vy.end());
vy.erase(unique(vy.begin(),vy.end()),vy.end());
int len=vy.size();
rt[0]=build(1,len);
for(int i=1;i<=n;i++)
{
int id=getid(a[i]);
rt[i]=update(id,1,len,rt[i-1]); //记录每个历史版本的根节点
}
for(int i=1;i<=m;i++)
{
int l,r,k;
cin>>l>>r>>k;
int id=query(rt[l-1],rt[r],1,len,k); //前缀和思想
//cout<<id<<endl;
cout<<vy[id-1]<<endl;
}
}
LCA
Tarjan求LCA
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10;
using i64 = long long;
int n,m,s;
vector<int> g[N];
vector<pair<int,int>> q[N];
bool st[N];
int p[N];
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
void add(int a,int b)
{
int aa=find(a);
int bb=find(b);
if(aa!=bb)
p[bb]=a;
return ;
}
int idx;
int ans[N];
void tarjan(int u,int f)
{
st[u]=true;
for(auto v : g[u])
{
if(v==f) continue;
if(!st[v])
{
tarjan(v,u);
p[v]=u;
st[v]=true;
}
}
for(auto w : q[u])
{
int v=w.first;
int id=w.second;
if(st[v])
{
ans[id]=find(v);
}
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
q[a].push_back({b,i});
q[b].push_back({a,i});
}
tarjan(s,0);
for(int i=1;i<=m;i++) cout<<ans[i]<<"\n";
}
倍增法求LCA
#include<bits/stdc++.h>
using namespace std;
using i64 = long long;
const int LOGN = 31;
const int N=5e5+10;
int n,q,s;
int dep[N],par[N][LOGN+1],val[N][LOGN+1];
vector<pair<int,int> > e[N];
void dfs(int u,int f)
{
dep[u] = dep[f] + 1;
for(auto p : e[u])
{
int v=p.first;
if(v==f) continue;
par[v][0]=u;
val[v][0]=p.second;
dfs(v,u);
}
}
//求两个点之间的距离最小值
int query(int u,int v)
{
int ans = 1<<30;
if(dep[u]>dep[v]) swap(u,v);
int d=dep[v]-dep[u];
for(int j=LOGN;j>=0;j--)
{
if(d&(1<<j))
{
ans=min(ans,val[v][j]);
v=par[v][j];
}
}
if(u==v) return ans;
for(int j=LOGN;j>=0;j--) if(par[u][j]!=par[v][j]){
ans=min(ans,min(val[u][j],val[v][j]));
u=par[u][j];
v=par[v][j];
}
ans=min(ans,min(val[u][0],val[v][0]));
return ans;
}
int LCA(int u,int v)
{
if(dep[u]>dep[v]) swap(u,v);
int d=dep[v]-dep[u];
for(int j=LOGN;j>=0;j--)
{
if(d&(1<<j))
{
v=par[v][j];
}
}
if(u==v) return u;
for(int j=LOGN;j>=0;j--) if(par[u][j]!=par[v][j]){
u=par[u][j];
v=par[v][j];
}
return par[u][0];
}
int main()
{
cin>>n>>q>>s;
for(int i=1;i<n; i++)
{
int u,v,w;
cin>>u>>v;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
dfs(s,0);
for(int j = 1;j<=LOGN;j++)
{
for(int u=1;u<=n;u++)
{
par[u][j]=par[par[u][j-1]][j-1];
//val[u][j]=min(val[u][j-1],val[par[u][j-1]][j-1]);
}
}
for(int i=1;i<=q;i++)
{
int u,v;
cin>>u>>v;
cout<<LCA(u,v)<<endl;
}
}
树链剖分求LCA
#include <bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
const int N = 5e5 + 3;
using i64 = long long;
int fa[N],hs[N],sz[N],id[N];
int top[N],dep[N],tot,l[N],r[N];
int n,m,s;
vector<int> e[N];
// 第一遍DFS,子树大小,重儿子,父亲,深度
void dfs1(int u,int f)
{
sz[u]=1;
hs[u]=-1;
fa[u]=f;
dep[u]=dep[f]+1;
for(auto v : e[u])
{
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(hs[u] == -1 || sz[hs[u]] < sz[v]) hs[u]=v;
}
}
// 第二遍DFS,每一个点DFS序,重链上的链头的元素
void dfs2(int u,int t)
{
l[u]=++tot;
top[u]=t;
id[tot]=u;
if(hs[u]!=-1)
{
dfs2(hs[u],t);
}
for(auto v : e[u])
{
if(v!=hs[u]&&v!=fa[u])
{
dfs2(v,v);
}
}
r[u]=tot;
}
int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]] < dep[top[v]]) v=fa[top[v]];
else u=fa[top[u]];
}
if(dep[u]<dep[v]) return u;
else return v;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(s,-1);
dfs2(s,s);
while(m--)
{
int a,b;
cin>>a>>b;
cout<<LCA(a,b)<<endl;
}
}
树链剖分
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
const int N=2e5+3;
using i64 = long long;
int n,q;
int l[N],r[N],id[N],dep[N],hs[N],sz[N];
int fa[N],top[N],a[N],tot;
vector<int> e[N];
struct info
{
int maxn,sums;
}tr[N*4];
void update(int p)
{
tr[p].maxn=max(tr[2*p].maxn,tr[2*p+1].maxn);
tr[p].sums=tr[2*p].sums+tr[2*p+1].sums;
}
info operator+(const info& a,const info& b)
{
return (info){max(a.maxn,b.maxn),a.sums+b.sums};
}
void build(int p,int l,int r)
{
if(l==r)
{
tr[p].maxn=a[id[l]],tr[p].sums=a[id[l]];
return ;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
update(p);
}
void modify(int p,int l,int r,int pos,int val)
{
if(l==r)
{
tr[p].maxn=val,tr[p].sums=val;
return ;
}
//cout<<p<<endl;
int mid=(l+r)/2;
if(pos<=mid) modify(2*p,l,mid,pos,val);
else modify(2*p+1,mid+1,r,pos,val);
update(p);
}
info query(int p,int l,int r,int ql,int qr)
{
//cout<<p<<endl;
if(ql==l&&qr==r)
{
return tr[p];
}
int mid=(l+r)/2;
if(qr<=mid) return query(2*p,l,mid,ql,qr);
else if(ql>mid) return query(2*p+1,mid+1,r,ql,qr);
else return query(2*p,l,mid,ql,mid)+query(2*p+1,mid+1,r,mid+1,qr);
}
void dfs1(int u,int f)
{
sz[u]=1;
hs[u]=-1;
fa[u]=f;
dep[u]=dep[f]+1;
for(auto v: e[u])
{
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(hs[u] == -1 || sz[hs[u]] < sz[v]) hs[u]=v;
}
//cout<<u<<endl;
}
void dfs2(int u,int t)
{
l[u]=++tot;
id[tot]=u;
top[u]=t;
if(hs[u]!=-1) dfs2(hs[u],t);
for(auto v : e[u])
{
if(v==hs[u]||v==fa[u]) continue;
dfs2(v,v);
}
r[u]=tot;
}
info check(int u,int v)
{
info ans={(int)-1e9,0};
while(top[u]!=top[v])
{
if(dep[top[u]]>dep[top[v]])
{
ans=ans+query(1,1,n,l[top[u]],l[u]);
u=fa[top[u]];
}
else
{
ans=ans+query(1,1,n,l[top[v]],l[v]);
v=fa[top[v]];
}
//cout<<v<<endl;
}
//cout<<"111"<<endl;
if(dep[u]>dep[v]) ans=ans+query(1,1,n,l[v],l[u]);
else ans=ans+query(1,1,n,l[u],l[v]);
return ans;
}
void solve()
{
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++) cin>>a[i];
cin>>q;
dfs1(1,0);
dfs2(1,1);
build(1,1,n);
//cout<<"ddddd"<<endl;
while(q--)
{
string op;
cin>>op;
if(op[0]=='C')
{
int u,t;
cin>>u>>t;
modify(1,1,n,l[u],t);
}
else
{
int u,v;
cin>>u>>v;
info ans=check(u,v);
if(op=="QMAX") cout<<ans.maxn<<endl;
else cout<<ans.sums<<endl;
}
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int T;
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
树上启发式合并
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N = 3e5+10;
using i64 = long long;
int n,m;
int c[N];
int l[N],r[N],id[N],sz[N],hs[N],tot;
vector<int> e[N];
int cnt[N]; //每一个颜色出现次数
int maxcnt; //众数出现次数
int sumcnt,ans[N]; //众数出现的和
void dfs_init(int u,int f)
{
l[u] = ++tot;
id[tot] = u;
sz[u] = 1;
hs[u] = -1;
for(auto v : e[u])
{
if(v==f) continue;
dfs_init(v,u);
sz[u] += sz[v];
if(hs[u] == -1 || sz[v] > sz[hs[u]]) hs[u] = v;
}
r[u] = tot;
}
void dfs_solve(int u,int f,bool keep)
{
for(auto v : e[u])
{
if(v != f && v != hs[u])
{
dfs_solve(v,u,false);
}
}
if(hs[u] != -1){
dfs_solve(hs[u],u,true);
//重儿子集合
}
auto add = [&](int x){
x=c[x];
cnt[x]++;
if(cnt[x] > maxcnt) maxcnt=cnt[x],sumcnt=0;
if(cnt[x] == maxcnt) sumcnt+=x;
};
auto del = [&](int x){
x=c[x];
cnt[x]--;
};
for(auto v : e[u]){
if(v!=f && v != hs[u]){ //v是轻儿子
// 把v子树里所有点加入到重儿子集合中
for(int x=l[v];x<=r[v];x++)
add(id[x]);
}
}
add(u);
ans[u]=sumcnt;
//把u 本身加入
if(!keep){
//清空
maxcnt=0;
sumcnt=0;
for(int x=l[u];x<=r[u];x++){
del(id[x]);
}
}
}
signed main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>c[i];
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs_init(1,0);
dfs_solve(1,0,0);
for(int i=1;i<=n;i++) cout<<ans[i]<<" \n"[i==n];
}
虚树
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<int> e[N];
int l[N],r[N],top[N],dep[N],fa[N],hs[N],id[N],sz[N],tot;
int h[N];
vector<int> g[N];
bool cmp(int a,int b)
{
return l[a]<l[b];
}
void dfs1(int u,int f)
{
sz[u]=1;
hs[u]=-1;
dep[u]=dep[f]+1;
fa[u]=f;
for(auto v : e[u])
{
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(hs[u] == -1 || sz[hs[u]]<sz[v]) hs[u]=v;
}
}
void dfs2(int u,int t)
{
l[u]=++tot;
id[tot]=u;
top[u]=t;
if(hs[u]!=-1) dfs2(hs[u],t);
for(auto v : e[u])
{
if(v==hs[u]||v==fa[u]) continue;
dfs2(v,v);
}
r[u]=tot;
}
int LCA(int u,int v)
{
while(top[u]!=top[v])
{
if(dep[top[u]]<dep[top[v]]) v=fa[top[v]];
else u=fa[top[u]];
}
if(dep[u]<dep[v]) return u;
else return v;
}
void build_virtual_tree()
{
sort(h+1,h+m+1,cmp); //把关键节点按照dfs序排序
vector<int> a;
for(int i=1;i<m;i++)
{
a.push_back(h[i]);
a.push_back(LCA(h[i],h[i+1])); //加入相邻两个点的LCA
}
a.push_back(h[m]);
sort(a.begin(),a.end(),cmp);
a.erase(unique(a.begin(),a.end()),a.end()); //排序去重
int len=a.size();
for(int i=0,lca;i<len-1;i++)
{
lca=LCA(a[i],a[i+1]);
g[lca].push_back(a[i+1]);
g[a[i+1]].push_back(lca);
}
}
void solve()
{
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
点分治
莫队
#include<bits/stdc++.h>
#define int long long
#define debug() cout<<"----------debug-------------"
using namespace std;
const int N=500010;
int n,q;
int c[N];
array<int,3> que[N];
int B=750; //分块的大小
int ans[N];
int tmp=0;
int ans2[N]; // 记录分母
int cnt[N]; //记录每一个数出现的次数
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>c[i];
for(int i=0;i<q;i++)
{
int l,r;
cin>>l>>r;
que[i]={l,r,i};
ans2[i]=(r-l)*(r-l+1)/2;
}
sort(que,que+q,[&](array<int,3> a,array<int,3> b){
int d=a[0]/B;
if(a[0] / B!=b[0] / B) return a[0] / B <b[0] /B;
if(d%2==1) return a[1]<b[1];
else return a[1]>b[1];
});
int l=1,r=0;
auto add = [&](int x){
tmp+=cnt[c[x]];
cnt[c[x]]++;
};
auto del = [&](int x){
cnt[c[x]]--;
tmp-=cnt[c[x]];
};
for(int i=0;i<q;i++)
{
if(que[i][1]==que[i][0])
{
ans[que[i][2]]=0;
continue;
}
while(r<que[i][1]) r++,add(r);
while(l>que[i][0]) l--,add(l);
while(r>que[i][1]) del(r),r--;
while(l<que[i][0]) del(l),l++;
ans[que[i][2]]=tmp;
}
for(int i=0;i<q;i++)
{
if(ans2[i]==0&&ans[i]==0)
{
cout<<"0/1"<<endl;
continue;
}
int d=__gcd(ans[i],ans2[i]);
cout<<ans[i]/d<<"/"<<ans2[i]/d<<endl;
}
return 0;
}
只增加的回滚莫队
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int n,m,q,k;
int a[N];
int B=750;
array<int,3> que[N];
using i64 = long long;
int cnt0[N];
int cnt[N];
int ans[N];
int L[N],R[N];
int bel[N];
void build()
{
int tot=n/B;
R[0]=0;
for(int i=1;i<=tot;i++)
{
L[i]=(i-1)*B+1;
R[i]=i*B;
}
if(R[tot]<n)
{
tot++;
L[tot]=R[tot-1]+1;
R[tot]=n;
}
for(int i=1;i<=tot;i++)
{
for(int j=L[i];j<=R[i];j++)
bel[j]=i;
}
}
int c[N];
void solve()
{
cin>>n>>q;
vector<int> res;
for(int i=1;i<=n;i++) cin>>a[i],bel[i]=0,ans[i]=0,res.push_back(a[i]);
//离散化,不然容易超时
sort(res.begin(),res.end());
res.erase(unique(res.begin(),res.end()),res.end());
for(int i=1;i<=n;i++)
{
c[i]=lower_bound(res.begin(),res.end(),a[i])-res.begin();
}
build();
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
que[i]={l,r,i};
}
//离线排序
sort(que+1,que+1+q,[&](array<int,3>& a,array<int,3>& b){
int d=bel[a[0]];
if(bel[a[0]] != bel[b[0]]) return bel[a[0]] < bel[b[0]];
//if(d%2==1) return a[1]<b[1];
//else return a[1]>b[1];
return a[1]<b[1];
});
int tmp=0,Ans=0;
auto del = [&](int x)
{
cnt[x]--;
};
auto add = [&](int x,int& Ans)
{
cnt[x]++;
if(1ll*cnt[x]*res[x]>Ans) Ans=1ll*cnt[x]*res[x];
};
int l=1,r=0,last_block=0,__l=0;
for(int i=1;i<=q;i++)
{
// 询问的左右端点同属于一个块则暴力扫描回答
if(bel[que[i][0]]==bel[que[i][1]])
{
for(int j=que[i][0];j<=que[i][1];j++) cnt0[c[j]]++;
for(int j=que[i][0];j<=que[i][1];j++)
ans[que[i][2]]=max(ans[que[i][2]],1ll*cnt0[c[j]]*a[j]);
for(int j=que[i][0];j<=que[i][1];j++) cnt0[c[j]]--;
continue;
}
// 访问到了新的块则重新初始化莫队区间,为空区间
if(bel[que[i][0]] != last_block)
{
while(r > R[bel[que[i][0]]]) del(c[r]),r--;
while(l < R[bel[que[i][0]]]+1) del(c[l]),l++;
Ans=0;
last_block=bel[que[i][0]];
}
// 扩展右端点
while(r<que[i][1]) ++r,add(c[r],Ans);
__l=l;
tmp=Ans;
// 扩展左端点
while(__l>que[i][0]) --__l,add(c[__l],tmp);
ans[que[i][2]]=tmp;
// 回滚
while(__l<l) del(c[__l]),++__l;
}
for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int _=1;
//cin>>_;
while(_--)
{
solve();
}
return 0;
}
只删减的回滚莫队
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int n,m,q,k;
int a[N];
int B=750;
array<int,3> que[N];
using i64 = long long;
int cnt0[N];
int cnt[N];
int ans[N],ans0=0;
int L[N],R[N];
int bel[N];
void build()
{
int tot=n/B;
R[0]=0;
for(int i=1;i<=tot;i++)
{
L[i]=(i-1)*B+1;
R[i]=i*B;
}
if(R[tot]<n)
{
tot++;
L[tot]=R[tot-1]+1;
R[tot]=n;
}
for(int i=1;i<=tot;i++)
{
for(int j=L[i];j<=R[i];j++)
bel[j]=i;
}
for(int i=1;i<=n;i++)
{
if(a[i]<=n+1) cnt[a[i]]++;
}while(cnt[ans0]) ans0++;
}
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i],bel[i]=0,ans[i]=0;
build();
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
que[i]={l,r,i};
}
//离线排序
sort(que+1,que+1+q,[&](array<int,3>& a,array<int,3>& b){
if(bel[a[0]] != bel[b[0]]) return bel[a[0]] < bel[b[0]];
return a[1]>b[1];
});
int tmp=0,Ans=0;
auto del = [&](int x,int& Ans)
{
if(x<=n+1) cnt[x]--;
if(cnt[x]==0) Ans=min(Ans,x);
};
auto add = [&](int x)
{
if(x<=n+1) cnt[x]++;
};
int l=1,r=n,last_block=0,__l=0;
for(int i=1;i<=q;i++)
{
// 询问的左右端点同属于一个块则暴力扫描回答
if(bel[que[i][0]]==bel[que[i][1]])
{
for(int j=que[i][0];j<=que[i][1];j++) cnt0[a[j]]++;
int tmp=0;
while(cnt0[tmp]) tmp++;
ans[que[i][2]]=tmp;
for(int j=que[i][0];j<=que[i][1];j++) cnt0[a[j]]--;
continue;
}
// 访问到了新的块则重新初始化莫队区间,为空区间
if(bel[que[i][0]] != last_block)
{
while(r < n) r++,add(a[r]);
while(l < L[bel[que[i][0]]]) del(a[l],ans0),l++;
Ans=ans0;
last_block=bel[que[i][0]];
}
// 扩展右端点
while(r>que[i][1]) del(a[r],Ans),r--;
__l=l;
tmp=Ans;
// 扩展左端点
while(__l<que[i][0]) del(a[__l],tmp),__l++;
ans[que[i][2]]=tmp;
// 回滚
while(__l>l) __l--,add(a[__l]);
}
for(int i=1;i<=q;i++) cout<<ans[i]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int _=1;
//cin>>_;
while(_--)
{
solve();
}
return 0;
}
带权并查集
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int p[N];
int dist[N]; //维护每一个点到根节点的距离
int ans[N]; //维护每一棵树的高度
int n;
int siz[N];
void init()
{
for(int i=1;i<=n;i++)
{
p[i]=i;
dist[i]=0;
}
}
//普通并查集
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]); //带路径压缩
return p[x];
}
void merge(int a,int b) //将b所在集合合并到a中
{
int aa=find(a);
int bb=find(b);
if(aa!=bb){
p[bb]=aa;
}
}
//带权并查集,维护每一个点到根节点的距离
int find(int x)
{
if(x!=p[x])
{
int t=find(p[x]);
dist[x]+=dist[t];
p[x]=t;
}
return p[x];
}
void merge(int a,int b) //假设b是根节点
{
int aa=find(a);
int bb=find(b);
if(aa!=bb)
{
p[bb]=aa;
dist[bb]=dist[a]+1;
ans[aa]=max(ans[aa],dist[b]+ans[b]);
}
}
ST表
#include<bits/stdc++.h>
using namespace std;
const int N=200010,M=18;
int dp[N][M];
int x[N];
int n;
void st()
{
for(int j=0;j<M;j++)
{
for(int i=1;i+(1<<j)-1<=n;i++)
{
if(!j) dp[i][0]=x[i];
else dp[i][j]=min(dp[i][j-1],dp[i + (1<<(j-1))][j-1]);
}
}
}
int query(int l,int r)
{
int len=r-l+1;
int k=log(len)/log(2);
return min(dp[l][k],dp[r-(1<<k)+1][k]);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>x[i];
int q;
cin>>q;
st();
while(q--)
{
int l,r;
cin>>l>>r;
cout<<query(l,r)<<endl;
}
return 0;
}
MEX
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
using i64 = long long;
int T;
int n,m,k,q;
int a[N];
struct node
{
int val;
}tr[N*4];
vector<PII> qu[N];
int ans[N];
set<int> s;
int cnt[N];
void solve()
{
cin>>n>>q;
for(int i=0;i<=n+1;i++) s.insert(i);
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=min(a[i],n + 1);
cnt[a[i]]++;
if(s.count(a[i])) s.erase(a[i]);
}
for(int i=1;i<=q;i++)
{
int k,x;
cin>>k>>x;
x=min(x,n+1);
cnt[a[k]]--;
if(cnt[a[k]]==0) s.insert(a[k]);
a[k]=x;
cnt[x]++;
if(s.count(x)) s.erase(x);
ans[i]=*s.begin();
}
for(int i=1;i<=q;i++)
cout<<ans[i]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
线段树上二分求MEX
#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
using i64 = long long;
int T;
int n,m,k,q;
int a[N];
struct node
{
int val;
}tr[N*4];
vector<PII> qu[N];
int ans[N];
void update(int p)
{
tr[p].val=min(tr[2*p].val,tr[2*p+1].val);
}
void modify(int p,int l,int r,int pos,int d)
{
if(l==r)
{
tr[p].val=d;
return ;
}
int mid=(l+r)/2;
if(pos<=mid) modify(2*p,l,mid,pos,d);
else modify(2*p+1,mid+1,r,pos,d);
update(p);
}
int search(int p,int l,int r,int d)
{
if(l==r) return l;
int mid=(l+r)/2;
if(tr[2*p].val<d) return search(2*p,l,mid,d);
else return search(2*p+1,mid+1,r,d);
}
void solve()
{
cin>>n>>q;
for(int i=1;i<=n;i++)
{
cin>>a[i];
a[i]=min(a[i],n + 1);
}
for(int i=1;i<=q;i++)
{
int l,r;
cin>>l>>r;
qu[r].push_back({l,i});
}
for(int r=1;r<=n;r++)
{
modify(1,0,n+1,a[r],r);
for(auto [l,id] : qu[r])
{
i64 d=search(1, 0, n+1, l) ;
ans[id]=d;
}
}
for(int i=1;i<=q;i++)
cout<<ans[i]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
扫描线
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<array<int,4>> e;
int ans=0;
vector<int> vx;
struct info
{
int minx,mincnt;
};
struct node
{
info val;
int tag;
}tr[N*8];
info operator+(const info& a, const info& b)
{
if(a.minx<b.minx)
return a;
else if(b.minx<a.minx) return b;
else
{
return (info){a.minx,a.mincnt+b.mincnt};
}
}
void update(int p)
{
tr[p].val=tr[2*p].val+tr[2*p+1].val;
}
void settag(int p,int t)
{
tr[p].tag+=t;
tr[p].val.minx+=t;
}
void pushdown(int p)
{
if(tr[p].tag)
{
int t=tr[p].tag;
settag(2*p,t);
settag(2*p+1,t);
tr[p].tag=0;
}
}
void build(int p,int l,int r)
{
if(l==r)
{
tr[p].val={0,vx[r]-vx[r-1]};
tr[p].tag=0;
return ;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
update(p);
}
void modify(int p,int l,int r,int ql,int qr,int tag)
{
if(l==ql&&r==qr)
{
settag(p,tag);
return ;
}
int mid=(l+r)/2;
pushdown(p);
if(qr<=mid) modify(2*p,l,mid,ql,qr,tag);
else if(ql>mid) modify(2*p+1,mid+1,r,ql,qr,tag);
else{
modify(2*p,l,mid,ql,mid,tag);
modify(2*p+1,mid+1,r,mid+1,qr,tag);
}
update(p);
}
info query(int p,int l,int r,int ql,int qr)
{
if(l==ql&&r==qr)
{
return tr[p].val;
}
int mid=(l+r)/2;
pushdown(p);
if(qr<=mid) return query(2*p,l,mid,ql,qr);
else if(ql>mid) return query(2*p+1,mid+1,r,ql,qr);
else return query(2*p,l,mid,ql,mid)+
query(2*p+1,mid+1,r,mid+1,qr);
update(p);
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
e.push_back({y1,1,x1,x2});
e.push_back({y2,-1,x1,x2});
vx.push_back(x1);
vx.push_back(x2);
}
sort(e.begin(),e.end());
sort(vx.begin(),vx.end());
vx.erase(unique(vx.begin(),vx.end()),vx.end());
m=vx.size()-1;
build(1,1,m);
int s=tr[1].val.mincnt;
int pre=0;
int now=0;
for(auto et : e)
{
int cnt=s;
now=et[0];
int d=tr[1].val.minx;
if(d==0) cnt-=tr[1].val.mincnt;
ans+=(now-pre)*cnt;
pre=now;
int x1=lower_bound(vx.begin(),vx.end(),et[2])-vx.begin()+1;
int x2=lower_bound(vx.begin(),vx.end(),et[3])-vx.begin();
if(x1>x2) continue;
modify(1,1,m,x1,x2,et[1]);
}
cout<<ans<<endl;
return ;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
树的直径
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
vector<PII> g[N];
int n,m;
int d[N];
int pl;
void solve()
{
cin>>n;
int sum=0;
for(int i=1;i<n;i++)
{
int a,b,c;
cin>>a>>b>>c;
sum+=c;
g[a].push_back({b,c});
g[b].push_back({a,c});
}
memset(d,0,sizeof d);
auto dfs = [&](auto dfs, int u, int p)->void {
for (auto [v, w] : g[u]) {
if (v == p) continue;
d[v] = d[u] + w;
if (d[v] > d[pl]) pl = v;
dfs(dfs, v, u);
}
};
dfs(dfs, 1,-1);
d[pl]=0;
dfs(dfs, pl,-1);
cout<<2*sum-d[pl]<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
T=1;
//cin>>T;
while(T--)
{
solve();
}
return 0;
}
树的重心
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int n;
vector<int> e[N];
int mx[N],sz[N];
vector<int> cen;
void dfs(int u,int f)
{
sz[u]=1;
for(auto v : e[u])
{
if(v==f) continue;
dfs(v,u);
sz[u]+=sz[v];
mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],n-sz[u]);
if(mx[u]<=n/2) cen.push_back(u);
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs(1,0);
cout<<mx[cen[0]]<<endl;
}
点分治
#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int N=3e5+10,mod=998244353;
typedef long long ll;
typedef pair<int,int> PII;
int T;
int n,m,k,q;
vector<PII> e[N];
bool del[N];
int mx[N],sz[N],ans,L;
void solve(int u,int s) //以u为根结点,s为子树的大小
{
int ms=s+1,root=-1;
function<void(int,int)> dfs1 = [&](int u,int par){ //找以u为根结点的子树的重心
sz[u]=1;
mx[u]=0;
for(auto [v,w] : e[u])
{
if(del[v] || v==par) continue;
dfs1(v,u);
sz[u]+=sz[v];
mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],s-sz[u]);
if(mx[u]<ms) ms=mx[u],root=u;
};
//root为找到的重心
dfs1(u,-1);
vector<int> d1,d2;
d1.push_back(0);
auto calc = [&](vector<int> &d){ //双指针求
sort(d.begin(),d.end());
int m=d.size();
int r=m-1;
ll ans=0;
for(int i=0;i<m;i++)
{
while(r>=0&&d[i]+d[r]>L) --r;
if(i<r) ans+=(r-i);
}
return ans;
};
function<void(int,int,int)> dfs2 =[&](int u,int par,int dep){
d1.push_back(dep);
d2.push_back(dep);
sz[u]=1;
for(auto [v,w] : e[u]){
if(del[v]||v==par) continue;
sz[u]+=sz[v];
dfs2(v,u,dep+w);
}
};
for(auto [v,w] : e[root]){
if(del[v]) continue;
d2.clear();
dfs2(v,root,w);
ans-=calc(d2); //容斥原理
}
ans+=calc(d1);
del[root] =true;
for(auto [v,w] : e[root]){
if(!del[v]) solve(v,sz[v]);
}
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<n;i++)
{
int u,v,w;
cin>>u>>v>>w;
e[u].push_back(make_pair(v,w));
e[v].push_back(make_pair(u,w));
}
cin>>L;
solve(1,n);
cout<<ans<<endl;
return 0;
}
图论
数学
动态规划