板子

最短路

dijkstra求最短路

//有向图
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<ll,ll>

const int Vertex_MAXN = 1e4 + 10;
const int Edge_MAXN = 5e5 + 10;
const ll inf = 9e18;

struct node{
	int v,nx;
	ll w;
}edge[Edge_MAXN];

int head[Vertex_MAXN],tot,n,m,s;
ll dis[Vertex_MAXN];

inline void add(int u,int v,ll w)
{
	edge[++tot].v = v;
	edge[tot].w = w;
	edge[tot].nx = head[u];
	head[u] = tot;
}

void dij(int s) {
	priority_queue<PII,vector<PII>,greater<PII>> q;
	fill(dis,dis+n+1,inf);
	dis[s] = 0;
	q.push(PII(0,s));
	while (!q.empty())
	{
		ll d = q.top().first;
		int u = q.top().second;
		q.pop();
		if (d != dis[u]) continue;
		for (int i = head[u];i;i = edge[i].nx)
		{
			PII y(d+edge[i].w,edge[i].v);
			if (dis[y.second]>y.first)
			{
				dis[y.second] = y.first;
				q.push(y);
			}
		}
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&s);
	for (int i = 1,u,v,w;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	dij(s);
	for (int i = 1;i<=n;i++) printf("%lld ",dis[i]);
	return 0;
}


spfa求最短路

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e4 + 10;

struct node{
	int v,nx,w; 
}edge[500010];

int head[MAXN],tot;

inline void add(int u,int v,int w)
{
	edge[++tot].v = v;
	edge[tot].nx = head[u];
	edge[tot].w = w;
	head[u] = tot;
}

int dis[MAXN],vis[MAXN],s;

void spfa()
{
	queue<int> q;
	fill(dis,dis+MAXN,2147483647);
	q.push(s);
	vis[s] = 1;
	dis[s] = 0;
	while (!q.empty())
	{
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (int i = head[u];i;i = edge[i].nx)
		{
			int v = edge[i].v;
			if (dis[v] > dis[u] + edge[i].w)
			{
				dis[v] = dis[u] + edge[i].w;
				if (!vis[v]) vis[v] = 1,q.push(v);
			}
		}
	}
} 

int main() 
{
	int n,m;
	scanf("%d%d%d",&n,&m,&s);
	for (int i = 1,u,v,w;i<=m;i++) 
	{
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);
	}
	spfa(); 
	for (int i = 1;i<=n;i++) printf("%d ",dis[i]);
	return 0;
}

网络流

dinic

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2e3;
const int inf = 2147483647;

int s,t;

struct node{
    int v,w,nx;
}edge[MAXN];

int tot,head[MAXN];

inline void add(int u,int v,int w)
{
    edge[tot].v = v;
    edge[tot].nx = head[u];
    edge[tot].w = w;
    head[u] = tot++;
}

int dis[MAXN];
queue<int> q;

bool bfs()
{
    memset(dis,-1,sizeof(dis));
    dis[s] = 0;
    q.push(s);
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = head[u];i != -1;i = edge[i].nx)
        {
            int v = edge[i].v;
            if (dis[v] == -1 && edge[i].w > 0)
            {
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}
 
int dfs(int u,int exp)
{
    if (u == t) return exp;
    int flow = 0,tmp = 0;
    for (int i = head[u];i != -1;i = edge[i].nx)
    {
        int v = edge[i].v;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0)
        {
            tmp = dfs(v,min(exp,edge[i].w));
            if (!tmp) continue;
            exp -= tmp;
            flow += tmp;
            edge[i].w -= tmp;
            edge[i^1].w += tmp;
            if (!exp) break;
        }
    }
    return flow;
}

void dinic()
{
    int ans = 0;
    while (bfs()) ans += dfs(s,inf);
    printf("%d",ans);
}

int main()
{
    int n;
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    for (int i = 1,u,v,w;i<=n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        add(u,v,w);
        add(v,u,0);
    }
    s = 65,t = 90;
    dinic();
    return 0;
}

二分图

二分图最大匹配

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e3;

int vis[MAXN][MAXN],cx[MAXN],cy[MAXN],n,m;
bool check[MAXN]; 

bool dfs(int u)
{
	for (int v = 1;v<=n;v++)
	{
		if (vis[u][v] && !check[v])//邻接矩阵存储,如果u-v之间有一条路 而且 v没有被访问过 
		{
			check[v] = 1;//标记v已经访问
			if (cy[v] == -1 || dfs(cy[v]))
			{
				cx[u] = v;
				cy[v] = u;
				return 1;
			}
		}
	}
	return 0;
}

int maxmatch()
{
	int ans = 0;
	memset(cx,-1,sizeof(cx));
	memset(cy,-1,sizeof(cy));
	for (int i = 1;i<=n;i++)//字典序从大到小 
	{
		if (cx[i] == -1)
		{
			memset(check,0,sizeof(check));
			ans += dfs(i);
		}
	}
	return ans;
}

int main()
{
	cin>>n;
	for (int i = 1;i<=n;i++)
	for (int j = 1;j<=n;j++)
	cin>>vis[i][j];
	int ans = maxmatch();
	cout<<ans;
}

二分图KM板子

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 3e2 + 10;

int a[MAXN][MAXN],d,n;
int c_girl[MAXN],c_boy[MAXN];//记录匹配对象 
int ex_girl[MAXN],ex_boy[MAXN];//记录男生和女生的期望 
bool vis_girl[MAXN],vis_boy[MAXN];//记录每一轮匹配匹配过的女生和男生 
int slack[MAXN]; // 记录每个汉子如果能被妹子倾心最少还需要多少期望值
 
bool dfs(int u)//匈牙利算法找增广路径 
{
	vis_girl[u] = 1;
	for (int i = 1;i<=n;i++)
	{
		if (vis_boy[i] == 0)//每一轮匹配 每个男生只尝试一次
		{
			int num = ex_girl[u] + ex_boy[i] - a[u][i];
			if (num == 0)//如果符合要求
			{
				vis_boy[i] = 1;
				if (c_boy[i] == 0 || dfs(c_boy[i])) //找到一个没有匹配的男生 或者该男生的妹子可以找到其他人
				{
					c_girl[u] = i;
					c_boy[i] = u;
					return 1;
				}
			}
			else slack[i] = min(slack[i],num);//slack可以理解为该男生要得到女生的倾心 还需多少期望值 取最小值 
		}
	}
	return 0; 
}

int KM() 
{
	memset(ex_girl,0,sizeof(ex_girl));// 每个女生的初始期望值是与她相连的男生最大的好感度
	memset(ex_boy,0,sizeof(ex_boy));// 初始每个男生的期望值为0
	memset(c_girl,0,sizeof(c_girl));// 初始每个男生都没有匹配的女生
	memset(c_boy,0,sizeof(c_boy));// 初始每个女生都没有匹配的男生
	for (int i = 1;i<=n;i++)
	for (int j = 1;j<=n;j++)
	ex_girl[i] = max(ex_girl[i],a[i][j]);// 每个女生的初始期望值是与她相连的男生最大的好感度
	
	for (int i = 1;i<=n;i++)  // 尝试为每一个女生解决归宿问题
	{
		fill(slack,slack + MAXN,2147483647);// 因为要取最小值 初始化为无穷大
		while (true) // 为每个女生解决归宿问题的方法是 :如果找不到就降低期望值,直到找到为止
		{
			memset(vis_girl,0,sizeof(vis_girl));
			memset(vis_boy,0,sizeof(vis_boy));// 记录每轮匹配中男生女生是否被尝试匹配过
			if (dfs(i)) break;// 找到归宿 退出
			
			 // 如果不能找到 就降低期望值
            // 最小可降低的期望值
			d = 2147483647;
			for (int j = 1;j<=n;j++)
			{
				if (!vis_boy[j]) d = min(d,slack[j]);
			}
			for (int j = 1;j<=n;j++)
			{
				if (vis_girl[j] == 1) ex_girl[j] -= d;//所有访问过的女生降低期望值
				if (vis_boy[j] == 1) ex_boy[j] += d;//所有访问过的男生增加期望值
				else slack[j] -= d; //没有访问过的boy 因为girl们的期望值降低,距离得到女生倾心又进了一步!
			}
		} 
	}
	// 匹配完成 求出所有配对的好感度的和
	int res = 0;
	for (int i = 1;i<=n;i++) res += a[i][c_girl[i]];
	return res;
}

int main() 
{
	cin>>n;
	for (int i = 1;i<=n;i++)
	for (int j = 1;j<=n;j++)
	scanf("%d",&a[i][j]);
	cout<<KM()<<'\n';
}

点分治

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 2e4 + 10;

struct node{
	int v,w,nx;
}edge[MAXN<<1];

int tot,head[MAXN];

inline void add(int u,int v,int w)
{
	edge[++tot].v = v;
	edge[tot].w = w;
	edge[tot].nx = head[u];
	head[u] = tot;
}

int sz[MAXN],maxsubtree,root,size1,vis[MAXN];//vis标记当前这个点有没有被作为分治点 

void getroot(int x,int fa)//找重心,把它作为当前树的根,是根据定义来求的 
{
	int maxn = 0;
	sz[x] = 1;
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa || vis[v]) continue;
		getroot(v,x);
		sz[x] += sz[v];
		maxn = max(maxn,sz[v]);//记录以x为根的最大子树大小 
	}
	maxn = max(maxn,size1 - sz[x]);//当以x为根时,其祖先结点也变成了x的子树 
	if (maxn < maxsubtree)//寻找最大子树最小 
	{
		maxsubtree = maxn;
		root = x;
	}
}

ll ans = 0,sum[4];

void dfs1(int x,int fa,int st)//统计子树中到分治点(重心)对3取模的路径有几条 
{
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa || vis[v]) continue;
		sum[(st + edge[i].w) % 3]++;
		dfs1(v,x,(st + edge[i].w) % 3);
	}
}

ll cal(int x,int st)//计算路径数量 
{
	sum[0] = sum[1] = sum[2] = 0;
	sum[st]++;
	ll res = 0;
	dfs1(x,0,st);//以重心为起始点,跑其子树的dfs,得到 到重心的路径权值%3为0,1,2的 数量 
	res = sum[1] * sum[2] * 2 + sum[0] * sum[0];//统计答案 
	return res;
}

void dfs(int x) 
{
	ans += cal(x,0);//计算经过当前点的路径的数量 
	vis[x] = 1;
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (vis[v]) continue;
		ans -= cal(v,edge[i].w);//容斥,去掉统计答案时子树中互相组成路径没有经过重心(分治点)的路径数
		//之所以要加上edge[i].w是因为 以重心为根,计算子树到重心的路径权值的时候,加上了这条边
		//所以以其儿子点为根计算时也要加上这条边
		maxsubtree = 2147483647; size1 = sz[v];
		getroot(v,0);//寻找子树的重心 
		dfs(root);//以子树的重心为根分治子树 
	}
}

int main()
{
	int n;
	scanf("%d",&n);
	for (int i = 1,u,v,w;i<n;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		add(u,v,w % 3);
		add(v,u,w % 3);
	}
	maxsubtree = 2147483647; size1 = n;
	getroot(1,0);//先以1为根,寻找树的重心 
	dfs(root);//依照重心分治 
	ll t = (ll)n * (ll)n;
	ll g = __gcd(ans,t);
	printf("%lld/%lld",ans / g,t / g);
	return 0;
}

树链剖分

重链剖分

#include<bits/stdc++.h>
using namespace std;
#define mem(a) memset(a,0,sizeof(a))

const int MAXN = 40100;
vector<int> g[MAXN];
int n,size1[MAXN],dfn[MAXN],top[MAXN],son[MAXN],fa[MAXN],d[MAXN],num = 0,aa[MAXN];
map<pair<int,int>,int> w;

//树链剖分
void dfs1(int x)
{
	d[x] = d[fa[x]] + 1;
	size1[x] = 1;
	for (int i = 0;i<g[x].size();i++)
	{
		int v = g[x][i];
		if (v == fa[x]) continue;
		fa[v] = x;
		dfs1(v);
		size1[x] += size1[v];
		if (size1[v] > size1[son[x]]) son[x] = v;
	}
}

void dfs2(int x,int tp)
{
	dfn[x] = ++num;
	top[x] = tp;
	aa[num] = w[make_pair(x,fa[x])];
	if (son[x]) dfs2(son[x],tp);
	for (int i = 0;i<g[x].size();i++)
		if (g[x][i] != fa[x] && g[x][i] != son[x]) dfs2(g[x][i],g[x][i]);
}
 
struct node{
	int l,r,lazy,num,nl,nr;
	int mid(){ return (r - l) / 2 + l;}
	void merge(node a,node b)//合并两个区间 
	{
		num = a.num + b.num;
		if (a.nr == b.nl && a.nr != -1 && b.nl != -1) num--;
		nl = a.nl; nr = b.nr;
	}
	void overturn() {swap(nr,nl);}//将区间顺序倒置 
	// void write() //检验时用 
	// {
	// 	printf("%d %d %d %d %d %d\n",l,r,lazy,num,nl,nr);
	// }
};

struct seg_tree{
	node tree[MAXN * 4];
	void build(int st,int ed,int x)
	//建树,lazy表示当前结点下的子节点需要更改的值,nl表示左端点的数字,nr表示右端点的数字,num表示有多少段颜色相同的 
	{
		tree[x].l = st;
		tree[x].r = ed;
		tree[x].lazy = -1;
		if (st == ed)
		{
			tree[x].nl = tree[x].nr = aa[st];
			tree[x].num = 1;
			if (st == 1)//建树的时候,一条边上两点,深度较大的那个点的dfn作为这条线段的dfn,所以根节点1没有对应的边 
			{
				tree[x].nl = tree[x].nr = -1;
				tree[x].num = 0;
			}
			return;
		}
		int mid = tree[x].mid();
		build(st,mid,x * 2);
		build(mid + 1,ed,x * 2 + 1);
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
	}
	void modify(int st,int ed,int x,int c)//区间修改值 
	{
		if (tree[x].l >= st && tree[x].r <= ed)
		{
			tree[x].nl = tree[x].nr = c;
			tree[x].num = 1;
			tree[x].lazy = c;
			return;
		}
		if (tree[x].lazy != -1)
		{
			tree[x*2].lazy=tree[x*2+1].lazy=tree[x*2].nl=tree[x*2].nr=tree[x*2+1].nl=tree[x*2+1].nr=tree[x].lazy;
			tree[x * 2].num = tree[x * 2 + 1].num = 1;
			tree[x].lazy = -1;
		}
		int mid = tree[x].mid();
		if (mid >= st) modify(st,ed,x * 2,c);
		if (ed > mid) modify(st,ed,x * 2 + 1,c);
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
	}
	node query(int st,int ed,int x,char c)//查询区间 
	{
		if (tree[x].l >= st && tree[x].r <= ed)
		{
			node temp = tree[x];
			if (c == 'L') temp.overturn();//如果是从起始点开始合并 
			return temp;
		}
		if (tree[x].lazy != -1)
		{
			tree[x*2].lazy=tree[x*2+1].lazy=tree[x*2].nl=tree[x*2].nr=tree[x*2+1].nl=tree[x*2+1].nr=tree[x].lazy;
			tree[x * 2].num = tree[x * 2 + 1].num = 1;
			tree[x].lazy = -1;
		}
		int mid = tree[x].mid();
		node ans;
		ans.nl = -1; ans.nr = -1; ans.num = 0;
		if (mid >= st) ans = query(st,ed,x * 2,c);
		if (ed > mid)
		{
			if (ans.num == 0) ans = query(st,ed,x * 2 + 1,c);
			else if (c == 'L') ans.merge(query(st,ed,x * 2 + 1,c),ans);
			else if (c == 'R') ans.merge(ans,query(st,ed,x * 2 + 1,c));
		}
		tree[x].merge(tree[x * 2],tree[x * 2 + 1]);
		return ans;
	}
}seg;


int mapping(int x,int y,int c)//c表示当前是修改还是查询 
{
	int fx = top[x],fy = top[y];
	node ans1,ans2;
	ans1.nl = ans1.nr = ans2.nl = ans2.nr = -1;//ans1表示从起始点开始合并,ans2表示从终点开始合并 
	ans1.num = ans2.num = 0;
	while (fx != fy)
	{
		if (d[fx] > d[fy])
		{
			if (c != -1) seg.modify(dfn[fx],dfn[x],1,c);
			else
			{
				if (ans1.num == 0) ans1 = seg.query(dfn[fx],dfn[x],1,'L'); //如果是从起点开始合并 
				else ans1.merge(ans1,seg.query(dfn[fx],dfn[x],1,'L'));
			}
			x = fa[fx];
			fx = top[x];
		}
		else
		{
			if (c != -1) seg.modify(dfn[fy],dfn[y],1,c);
			else
			{
				if (ans2.num == 0) ans2 = seg.query(dfn[fy],dfn[y],1,'R');//如果是从终点开始合并 
				else ans2.merge(seg.query(dfn[fy],dfn[y],1,'R'),ans2);
			}
			y = fa[fy];
			fy = top[y];
		}
	}
	if (x == y)//如果两条路径相交在同一个点 
	{
		if (c != -1) return 0;
		if (ans1.num == 0) return ans2.num;
		else if (ans2.num == 0) return ans1.num;
		ans1.merge(ans1,ans2);
		return ans1.num;
	}
	if (d[x] < d[y])//y在下面
	{
		if (c != -1)
		{
			seg.modify(dfn[x] + 1,dfn[y],1,c);
			return 0;
		}
		if (ans1.num == 0) ans1 = seg.query(dfn[x] + 1,dfn[y],1,'R');
		else ans1.merge(ans1,seg.query(dfn[x] + 1,dfn[y],1,'R'));
		if (ans2.num == 0) return ans1.num;
		else ans1.merge(ans1,ans2);
	}
	else//x在下面 
	{
		if (c != -1)
		{
			seg.modify(dfn[y] + 1,dfn[x],1,c);
			return 0;
		}
		if (ans1.num == 0) ans1 = seg.query(dfn[y] + 1,dfn[x],1,'L');
		else ans1.merge(ans1,seg.query(dfn[y] + 1,dfn[x],1,'L'));
		if (ans2.num != 0) ans1.merge(ans1,ans2);
	}
	return ans1.num;
}

int main()
{
	int n,p;
	while (scanf("%d%d",&n,&p) != EOF) 
	{
		for (int i = 1,u,v,t;i<=n-1;i++)
		{
			scanf("%d%d%d",&u,&v,&t);
			w[make_pair(u,v)] = t;
			w[make_pair(v,u)] = t;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		dfs1(1);
		dfs2(1,1);
		char c[10];
		seg.build(1,n,1);
		while (p--)
		{
			scanf("%s",&c);
			if (c[0] == 'Q')
			{
				int u,v;
				scanf("%d%d",&u,&v);
				if (u == v)  printf("0\n");
				else printf("%d\n",mapping(u,v,-1));
			}
			else if (c[0] == 'C')
			{
				int u,v,t;
				scanf("%d%d%d",&u,&v,&t);
				int temptemp = mapping(u,v,t);
			}
		}
		for (int i = 1;i<=n;i++) g[i].clear();
		mem(son); mem(size1); mem(d); mem(fa); mem(dfn); mem(top); mem(aa);
		num = 0;
		w.clear();
	}
}

长链剖分

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e6 + 10;

struct node{
	int u,v,nx;
}edge[MAXN<<1];

int tot,head[MAXN];

inline void add(int u,int v)
{
	edge[++tot].v = v;
	edge[tot].nx = head[u];
	head[u] = tot;
}

int len[MAXN],son[MAXN],fa[MAXN],num;

void dfs1(int x) 
{
	len[x] = 1;
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa[x]) continue;
		fa[v] = x;
		dfs1(v);
		len[x] = max(len[x],len[v] + 1);
		if (len[v] > len[son[x]]) son[x] = v;
	} 
}

int *dp[MAXN],tmp[MAXN],*id = tmp;
int ans[MAXN];

void dfs2(int x)
{
	dp[x][0] = 1;
	if (son[x])
	{
		dp[son[x]] = dp[x] + 1;
		dfs2(son[x]);
		ans[x] = ans[son[x]] + 1;
	}
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa[x] || v == son[x]) continue;
		dp[v] = id;
		id += len[v];
		dfs2(v);
		for (int j = 0;j<len[v];j++)
		{
			dp[x][j + 1] += dp[v][j];
			if (dp[x][j+1] > dp[x][ans[x]] || (dp[x][j+1] == dp[x][ans[x]] && j + 1 < ans[x]))
			ans[x] = j + 1;
		}
	}
	if (dp[x][ans[x]] == 1)  ans[x] = 0;
}

int main()
{
	int n;
	cin>>n;
	for (int i = 1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs1(1);
	dp[1] = id; id += len[1];
	dfs2(1);
	for (int i = 1;i<=n;i++) printf("%d\n",ans[i]);
}

LCA

树上倍增求LCA

#include<vector>
#include<cstring>
#include<stdio.h>
using namespace std;
#define mem(a) memset(a,0,sizeof(a))

const int Vertex_MAXN = 1e4 + 10; 
const int DEEP = 15;

vector<int> g[Vertex_MAXN];

int up[Vertex_MAXN][DEEP] = {},d[Vertex_MAXN] = {};

void dfs(int x)
{
	d[x] = d[up[x][0]] + 1;
	for (int i = 1;i<=DEEP-1;i++) up[x][i] = up[up[x][i-1]][i-1];
	for (int i = 0;i<g[x].size();i++)
	if (g[x][i] != up[x][0])
	{
		up[g[x][i]][0] = x;
		dfs(g[x][i]);
	}
}

int LCA(int x,int y)
{
	if (d[x] < d[y]) swap(x,y);
	int dis = d[x] - d[y];
	for (int i = DEEP-1;i>=0;i--)
		if (dis & (1<<i)) x = up[x][i];
	if (x == y) return x;
	for (int i = DEEP-1;i>=0;i--)
	{
		if (up[x][i] != up[y][i])
		{
			x = up[x][i],y = up[y][i];
		}
	}
	return up[x][0];
}

int main()
{
	int n,in[11000] = {};
	scanf("%d",&n);
	for (int i = 1,u,v;i<=n-1;i++)
	{
		scanf("%d%d",&u,&v);//规定前者为后者的父节点 
		g[u].push_back(v);
		in[v]++;
	}
	int u,v;
	for (int i = 1;i<=n-1;i++)
	{
		if (in[i] == 0)
		{
			dfs(i);
			break;
		}
	}
	scanf("%d%d",&u,&v);//询问u和v的LCA 
	printf("%d",LCA(u,v));
	mem(up); mem(d);
	for (int i = 1;i<=n-1;i++) g[i].clear();
	return 0;
} 

线段树

树状数组

树状数组维护区间和

//树状数组维护区间和 
#include<iostream>
#define ll long long
using namespace std;

int lowbit(int x)
{
	return x&-x;
}

int n;
ll a[50001] = {},b[50001] = {};

void change(ll x,ll w)
{
	while (x <= n)
	{
		b[x] += w;
		x +=lowbit(x);
	}
}

ll sum(int x)
{
	ll num = 0;
	while (x > 0)
	{
		num+=b[x];
		x -=lowbit(x);
	}
	return num;
}

int main()
{
	scanf("%d",&n);
	for (int i = 1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		a[i] += a[i-1];
		b[i] = a[i] - a[i - lowbit(i)];
	}
	char c[10] = {};
	scanf("%s",c);
	while (c[0] != 'E')
	{
		//E结束 Query询问x~y的区间和 Add在x处+y Sub在x处-y 
		int i,j;
		scanf("%d%d",&i,&j);
		if (c[0] == 'Q')
		{
			ll t = sum(j) - sum(i-1) ;
			printf("%lld\n",t);
		}
		else if (c[0] == 'A') change(i,j);
		else change(i,-j);
		scanf("%s",c);
	}
} 

树状数组维护区间最大值

//树状数组维护区间最大值(不可有修改操作) 
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 3e6 + 10;
#define ll long long

ll treemax[MAXN],a[MAXN];
int n;

inline int lowbit(int x)
{
	return x & -x;
}

void insert(ll v,int x)
{
	while (x <= n)
	{
		treemax[x] = max(v,treemax[x]);
		x += lowbit(x);
	}
}

ll query(int l,int r)
{
	ll res = -9e18;
	while (r >= l)
	{
		if (r - lowbit(r) < l)
		{
			res = max(res,a[r]);
			r--;
		}
		else
		{
			res = max(res,treemax[r]);
			r -=lowbit(r);
		}
	}
	return res;
}

int main()
{
	int m;
	scanf("%d%d",&n,&m);
	fill(treemax,treemax+MAXN,-9e18);
	for (int i = 1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		insert(a[i],i);
	}
	int l,r;
	while (m--)
	{
		scanf("%d%d",&l,&r);
		printf("%lld\n",query(l,r));
	}
	return 0;
}

zkw线段树

//zkw单点修改,区间查询
#include<iostream>
#include<stdio.h>
#include<cstring>
#include<math.h>
using namespace std;
const int NUM = 50000;

int m,tree[NUM * 4];

void modify(int n,int v)
{
	for (tree[n += m] += v,n>>=1;n;n>>=1) tree[n] = tree[n * 2] + tree[n * 2 + 1];
}

int query(int st,int ed)
{
	int ans = 0;
	for (int l = m + st - 1,r = m + ed + 1;l ^ r ^ 1;l>>=1,r>>=1)
	{
		if (~l & 1) ans += tree[l ^ 1];
		if (r & 1) ans += tree[r ^ 1];
	}
	return ans;
}

int main()
{
	memset(tree,0,sizeof(tree));
	int n;
	cin>>n;
	m = log(n) / log(2);
	if (pow(2,m) < n) m++;
	m = pow(2,m);
	for (int i = m + 1;i<=m + n;i++) scanf("%d",&tree[i]);
	for (int i = m - 1;i;i--) tree[i] = tree[i * 2] + tree[i * 2 + 1];
	char c[10];
	while (1)
	{
		//E结束 Query询问x~y的区间和 Add在x处+y Sub在x处-y 
		int x,y;
		scanf("%s",c);
		if (c[0] == 'E') break;
		scanf("%d%d",&x,&y);
		if (c[0] == 'Q') printf("%d\n",query(x,y));
		else if (c[0] == 'A') modify(x,y);
		else modify(x,-y);
	}
	return 0;
}

主席树

#include<bits/stdc++.h>
using namespace std;

const int MAXN = 1e6 + 10;

struct node{
	int u,v,nx;
}edge[MAXN<<1];

int tot,head[MAXN];

inline void add(int u,int v)
{
	edge[++tot].v = v;
	edge[tot].nx = head[u];
	head[u] = tot;
}

int len[MAXN],son[MAXN],fa[MAXN],num;

void dfs1(int x) 
{
	len[x] = 1;
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa[x]) continue;
		fa[v] = x;
		dfs1(v);
		len[x] = max(len[x],len[v] + 1);
		if (len[v] > len[son[x]]) son[x] = v;
	} 
}

int *dp[MAXN],tmp[MAXN],*id = tmp;
int ans[MAXN];

void dfs2(int x)
{
	dp[x][0] = 1;
	if (son[x])
	{
		dp[son[x]] = dp[x] + 1;
		dfs2(son[x]);
		ans[x] = ans[son[x]] + 1;
	}
	for (int i = head[x];i;i = edge[i].nx)
	{
		int v = edge[i].v;
		if (v == fa[x] || v == son[x]) continue;
		dp[v] = id;
		id += len[v];
		dfs2(v);
		for (int j = 0;j<len[v];j++)
		{
			dp[x][j + 1] += dp[v][j];
			if (dp[x][j+1] > dp[x][ans[x]] || (dp[x][j+1] == dp[x][ans[x]] && j + 1 < ans[x]))
			ans[x] = j + 1;
		}
	}
	if (dp[x][ans[x]] == 1)  ans[x] = 0;
}

int main()
{
	int n;
	cin>>n;
	for (int i = 1,u,v;i<n;i++)
	{
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	dfs1(1);
	dp[1] = id; id += len[1];
	dfs2(1);
	for (int i = 1;i<=n;i++) printf("%d\n",ans[i]);
}

数论

素数

素数筛

#include<iostream>
#include<math.h>
using namespace std;

const int MAX_N = 1000000;

int vis[MAX_N] = {},prime[MAX_N] = {},len = 0;

void init(int n)
{
	for (int i = 2;i<=n;i++)
	{
		if (vis[i] == 0) prime[++len] = i;
		for (int j = 1;j<=len;j++)
		{
			if (prime[j] * i > n) break;
			vis[prime[j] * i] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

int main()
{
	init(MAX_N);
	return 0; 
} 

图论

Tarjan

tarjan求环的个数和点的染色

#include<bits/stdc++.h> 
using namespace std;
const int MAXN = 2e2 + 10;
 
vector<int> g[MAXN];
int dfn[MAXN],low[MAXN],s[MAXN],vis[MAXN],num,slen,scnt,col[MAXN];
 
void tarjan(int u)
{
	low[u] = dfn[u] = ++num;
    //low存u的子树里所能到达的dfn最小的点
	s[++slen] = u;//s为栈
    vis[u] = 1;//标记u已经放到了栈里
	for (int i = 0;i<g[u].size();i++)
	{
		int v = g[u][i];
		if (!dfn[v])//如果v没有访问过
		{
			tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if (vis[v]) low[u] = min(low[u],dfn[v]);
        //一旦遇到已入栈的点,就将该点作为连通量的根
        //这里用dfn[e[i].v]更新的原因是:这个点可能
        //已经在另一个强连通分量中了但暂时尚未出栈
        //所以now不一定能到达low[e[i].v]但一定能到达
        //dfn[e[i].v].
	}
	if (dfn[u] == low[u])
	{
		scnt++;//环的数量
		do
		{
            vis[s[slen]] = 0;//出栈
            col[s[slen]] = scnt;//染色
		}while (s[slen--] != u);
	}
}

int main()
{
    int p;
    cin>>p;
    while (p--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for (int i = 1,u,v;i<=m;i++)
        {
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
        }
        for (int i = 0;i<n;i++)
        if (dfn[i] == 0) tarjan(i);
        for (int i = 0;i<n;i++) g[i].clear();
	    printf("%d\n",scnt);
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(s,0,sizeof(s));
        memset(vis,0,sizeof(vis));
        scnt = num = 0;
    }
}

tarjan求无向图割边和割点

//tarjan求无向图割边和割点
#include<bits/stdc++.h>
using namespace std;

const int MAXN = 2e4 + 10;

vector<int> g[MAXN];

int dfn[MAXN],low[MAXN],num,fa[MAXN];

struct EDGE{
    EDGE(int a = 0,int b = 0):u(a),v(b){}
    int u,v;
};

vector<EDGE> cutedge;
vector<int> cutnode;

bool cmp(EDGE a,EDGE b) {return a.u == b.u?a.v<b.v:a.u<b.u;}

void tarjan(int u)
{
    dfn[u] = low[u] = ++num;
    bool flag = false;
    int son = 0;
    for (int i = 0;i<g[u].size();i++)
    {
        int v = g[u][i];
        if (v == fa[u]) continue;
        if (!dfn[v])
        {
            son++;
            fa[v] = u;
            tarjan(v);
            low[u] = min(low[u],low[v]);
            if (low[v] >= dfn[u]) flag = true;
            //判断是否存在子节点只能通过u访问到u的祖先
            if (low[v] > dfn[u]) cutedge.push_back(EDGE(min(u,v),max(v,u)));
            //判断割边
        }
        else low[u] = min(low[u],dfn[v]);
    }
    if ((fa[u] == 0 && son >= 2) || (fa[u] != 0 && flag)) cutnode.push_back(u);
    //判断割点
    //u是根节点且u有两个连通分量则u是割点
    //u不是根节点,u存在一个子节点只能通过u访问到u的祖先
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i = 1,u,v;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        g[u].push_back(v);
        g[v].push_back(u);
    }
    for (int i = 1;i<=n;i++)
    if (dfn[i] == 0) tarjan(i);
    sort(cutedge.begin(),cutedge.end(),cmp);
    sort(cutnode.begin(),cutnode.end());
    //割点从小到大输出
    //割边(u,v),u<v,按照u为第一关键字,v为第二关键字排序
    if (cutnode.size() == 0) printf("Null\n");
    else
    {
        printf("%d",cutnode[0]);
        for (int i = 1;i<cutnode.size();i++) printf(" %d",cutnode[i]);
    }
    puts("");
    for (int i = 0;i<cutedge.size();i++)
    {
        printf("%d %d\n",cutedge[i].u,cutedge[i].v);
    }
    return 0;
}
posted @ 2021-12-24 17:14  Un-Defined  阅读(50)  评论(0编辑  收藏  举报