ACM演算法模版

Violet_fan發表於2024-08-31

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>
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;
} 

圖論

數學

動態規劃