zoukankan      html  css  js  c++  java
  • 板子

    模板整理

    二分(返回第一个等于x的元素的下标)

    int found(int a[],int left,int right,int x) {
    	while (left < right) {
    		int mid = (right + left) >> 1;
    		if (a[mid] < x) left = mid + 1;
    		else right = mid;
    	}
    	return left;
    }
    

    链式前向星

    struct Edge {
    	int next, to;
    	LL dis;
    }edges[maxn];
    
    void add_edge(int from, int to, LL dis) {
    	num++;
    	edges[num].next = head[from];
    	edges[num].to = to;
    	edges[num].dis = dis;
    	head[from] = num;
    }
    
    for (int i = head[u]; i != 0; i = edges[i].next) {
      ...
    }
    

    邻接矩阵

    struct Edge {
    	int from, to, dist;
    	//边的起点,终点,长度
    	Edge(int u,int v,int d):from(u),to(v),dist(d){}
    	//构造函数,用于边的初始化
    };
    
    struct HeapNode {
    	int d, u;//将结点的d值与结点捆绑在一起形成结构体,当然也可以用pair<int,int>代替
    	bool operator < (const HeapNode& rhs) const {
    		return d > rhs.d;
    		//当d>rhs.d为真时,优先级this<rhs.d成立,即d值小的优先级更大
    	}
    };
    
    void init(int n) {//初始化
      num=0;//边在vector中的编号
      for (int i = 0; i < n; i++) G[i].clear();
      edges.clear();
      //个人习惯,为了计数都能从1开始,先塞一个无关紧要的元素
      edges.push_back(Edge(0, 0, 0));
      //但G[i]计数还是从0开始
    }
    
    void AddEdge(int from, int to, int dist) {
      edges.push_back(Edge(from, to, dist));
      //调用Edge结构体中的构造函数,生成一条边并加入到Edge中
      G[from].push_back(++num);
    }
    

    最短路(Floyd)

    //初始化
    for (int i = 0;i < maxn ; i++) for (int j = 0;j< maxn ; j++) 
    		d[i][j] = (i == j) ? 0 : INF;
    
    for (int k = 0; k < n; k++)
      for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
          d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    

    最短路(SPFA)

    int cnt[maxn];
    bool inq[maxn];
    int d[maxn];
    
    bool spfa(int s) {
        queue<int>Q;
        memset(inq, 0, sizeof(inq));//标记结点是否在队列中
        memset(cnt, 0, sizeof(cnt));
        for (int i = 0; i < n; i++) d[i] = INF;
        d[s] = 0;
        inq[s] = true;//标记结点s已在队列中
        Q.push(s);
        while (!Q.empty()) {
            int u = Q.front(); Q.pop();
            inq[u] = false;//标记已不在队列
            for (int i = 0; i < G[u].size(); i++) {
                Edge& e = Edges[G[u][i]];//遍历以结点u为起点的有向边
                if (d[u]<INF && d[u] + e.dist<d[e.to]) {
                    d[e.to] = d[u] + e.dist;//松弛
                    p[e.to] = G[u][i];//记录父节点
                    if (!inq[e.to]) {//只要不在队列中就可以入队
                        Q.push(e.to);
                        inq[e.to] = true;
                        if (++cnt[e.to] > n) return false;
                        //如果某个点迭代了超过n次,说明存在可以无限缩短的最短路,即负环
                    }
                }
            }
        }
        return true;
    }
    

    最短路(Dijkstra)

    void dijkstra(int s){
      priority_queue<HeapNode>Q;
      for (int i = 0; i < n; i++) d[i] = INF;
      d[s] = 0;
      memset(done, 0, sizeof(done));
      Q.push( HeapNode{ 0, s } );
      //HeapNode这个名称不要括起来,否则在VS中会有奇怪的报错
      while (!Q.empty()) {
        HeapNode x = Q.top(); Q.pop();
        //d值最小的结点出队
        int u = x.u;
        //取该结点的起点
        if (done[u]) continue;
        for (int i = 0; i < G[u].size(); i++) {//遍历以u为起点的所有边
          Edge& e = edges[G[u][i]];
          //用G[u][i]取得具体某条边的编号,再用这个编号去找这条边的结构体,获得边的信息
          if (d[u] + e.dist < d[e.to] ) {
            d[e.to] = d[u] + e.dist;
            //更新边的终点的d值
            p[e.to] = G[u][i];
            //维护最短路中连接这个结点的上一条边的编号
            //注意这里记录的是边而非结点
            Q.push(HeapNode{ d[e.to],e.to });
          }
        }
        done[u] = true;
        //标记起点为u的所有边均已访问
      }
    }
    

    最小生成树(Kruskal)

    //并查集
    int find(int x) {return p[x] == x ? x : p[x] = find(p[x]);}
    
    int u[maxn], v[maxn];//第i条边的两个端点的序号
    int w[maxn];//第i条边的权值
    int cmp(const int i, const int j) { return w[i] < w[j]; }
    int find(int x) {return p[x] == x ? x : p[x] = find(p[x]);}
    int Kruskal() {
    	int ans = 0;
    	for (int i = 0; i < n; i++) p[i] = i;
    	for (int i = 0; i < m; i++) r[i] = i;
    	sort(r, r + m, cmp);
    	//实际上是对w数组排序,但将结果顺序的下标保存在r数组中
        //如w[4]={4,2,1,0},排序后r数组储存的是{3,2,1,0}
    	for (int i = 0; i < m; i++) {
    		int e = r[i];//取边(u,v),记为e
    		int x = find(u[e]);//找出e的一个端点u的所在集合编号
    		int y = find(v[e]);//找出e的另一个端点v的所在集合编号
    		if (x != y) {//如果不在同一个集合
    			ans += w[e];//最小生成树的总权值更新
    			p[x] = y;//合并
    		}
    	}
    	return ans;
    }
    

    线段树

    //存储
    struct Tree {
    	int left, right;
    	long long value, lazy;//结点维护的值及懒标记
    }tree[4*maxn+2];//注意数组大小至少要开到区间长度的四倍大
    
    //建树(以节点值为区间和为例)
    void build(int left, int right, int index) {
      //编号为index的结点维护的区间所对应的原数组下标为left到right
    	tree[index].left = left;
    	tree[index].right = right;
    	if (left == right) {//到达叶子结点
    		tree[index].value = a[l];
    		return;
    	}
    	int mid = (right + left) / 2;
    	build(left, mid, index * 2);
    	build(mid + 1, right, index * 2 + 1);
    	tree[index].value = tree[index * 2].value + tree[index * 2 + 1].value;
    	//不是叶子结点,则其值为左子树加右子树
    }
    
    //懒标记
    void spread(int i) {
    	if (tree[i].lazy) {//如果该结点有懒标记
    		int lc = i * 2, rc = i * 2 + 1;//左右子结点的下标
    		//括号里算的是子结点的区间长度,乘以lazy就是将相关叶子结点加上lazy之后的新的和
    		tree[lc].value += tree[i].lazy * (tree[lc].right - tree[lc].left + 1);
    		tree[rc].value += tree[i].lazy * (tree[rc].right - tree[rc].left + 1);
    		//为结点的左右结点打上标记
    		tree[lc].lazy += tree[i].lazy;
    		tree[rc].lazy += tree[i].lazy;
    		//标记下传之后将该结点的懒标记清零
    		tree[i].lazy = 0;
    	}
    }
    
    //区间修改
    void change(int i, int x, int y, int z) {
    	//要给区间[x,y]加上一个数z,先从根结点i向下找相应的结点
    	if (x <= tree[i].left && y >= tree[i].right) {
    		//如果[x,y]完全覆盖到了该结点表示的区间,就执行更新
    		tree[i].value += z * (tree[i].right - tree[i].left + 1);
    		//暂时只更新这个结点的值
    		tree[i].lazy += z;
    		//打上懒标记,表示其子结点的值仍待更新
    		return;
    		//直接返回上一层
    	}
      else{	//如果[x,y]没有完全覆盖该结点表示的区间,则继续向下找
        spread(i);
        //考虑到该结点可能有需要下放的懒标记,先将懒标记下放
        int mid = tree[i].left + (tree[i].right - tree[i].left) / 2;
        //防溢出的写法
        if (x <= mid) change(i * 2, x, y, z);
        //如果[x,y]覆盖到了左子结点,更新
        if (y > mid) change(i * 2 + 1, x, y, z);
        //如果[x,y]覆盖到了右子结点,更新
        tree[i].value = tree[i * 2].value + tree[i * 2 + 1].value;
        //最后更新该结点的值
        }
    }
    
    //区间查询
    int ask(int i, int x, int y) {
    	//询问区间[x,y]的和,先从根节点i向下找
    	if (x <= tree[i].left && y >= tree[i].right) return tree[i].value;
    	////如果[x,y]完全覆盖到了该结点表示的区间,返回这个结点的值(即结点表示的区间的和)
    	else {
    		int ans = 0;
    		spread(i);
    		//下放懒标记
    		int mid = tree[i].left + (tree[i].right - tree[i].left) / 2;
    		//防溢出的写法
    		if (x <= mid) ans += ask(i * 2, x, y);
    		if (y > mid) ans += ask(i * 2 + 1, x, y);
    		return ans;
    	}
    }
    

    树状数组

    int n;
    int a[1005],tree[1005]; //原数组和树状数组
    
    int lowbit(int x) return x&(-x);
    
    //修改
    void updata(int i,int p){    //在i位置加上p
        while(i <= n){
            tree[i] += p;//更新受影响的tree[i]
            i += lowbit(i);
        }
    }
    
    //查询
    int getsum(int i){        //求区间1到i的和
        int res = 0;
        while(i > 0){
            res += tree[i];
            i -= lowbit(i);
        }
        return res;
    }
    

    数位DP

    LL dp[20][][];
    //维数视情况决定,比如在【不要62】中,dp状态要区分有没有6
    int a[20];
    //给出数字范围在10^18以内可用,位数更多就扩大一下
    
    LL dfs(int pos,bool lead,bool limit){
    //pos当前枚举的位置,lead有无前导零,limit有无最高位限制
        LL ans = 0;
        if (pos == -1) return 1;
        if (!limit && !lead && dp[pos] != -1) return dp[pos];
      	//无前导零和最高位限制才能使用已储存的状态
        int end = limit ? a[pos] : 9;
        for (int i = 0; i <= end; i++) {
            if (不合法的情况(除0以外)) continue;
            if (!lead && (!i)) continue;
            //当前位为是零但不是前导零,也不合法,跳过(如果0不合法的话)
            ans += dfs(pos - 1, lead&&(!i), limit && i == end);
            //当前位是合法情况,继续搜
        }
        if (!limit&&!lead)  dp[pos] = ans;
        //没有前导零也没有最高位限制,说明本次统计结果是通用的
        return ans;
    }
    
    LL part(LL x){
        int len = 0; LL k = x;
        while (k) { a[len++] = k % 10; k /= 10; }
        memset(dp, -1, sizeof (dp));
        return dfs(len - 1, true, true);
      	//如果涉及到前导零,一般刚开始时都是默认有的
    }
    
    //注意l=0的时候不能用,要特判
    LL result(LL l , LL r) {
    	  return part(r) - part(l-1);
    }
    

    LCA(倍增)

    int num = 0;
    int head[maxn], depth[maxn], lg[maxn], fathers[maxn][20];
    
    struct Edge {
    	int next, to;
    }edges[2 * maxn];
    
    void add_edge(int u, int v) {
    	num++;
    	edges[num].to = v;
    	edges[num].next = head[u];
    	head[u] = num;
    }
    
    void dfs(int u, int u_father) {
    	depth[u] = depth[u_father] + 1;
    	fathers[u][0] = u_father;
    	for (int i = 1; (1 << i) <= depth[u]; i++)
    		fathers[u][i] = fathers[fathers[u][i - 1]][i - 1];
    	for (int i = head[u]; i; i = edges[i].next) {
    		if (edges[i].to != u_father) dfs(edges[i].to, u);
    	}
    }
    
    int LCA(int u, int v) {
    	if (depth[u] < depth[v]) swap(u, v);
    	while (depth[u] > depth[v])
    		u = fathers[u][lg[depth[u] - depth[v]] - 1];
    	if (u == v) return u;
    	for (int k = lg[depth[u]] - 1; k >= 0; k--) {
    		if (fathers[u][k] != fathers[v][k]) {
    			u = fathers[u][k];
    			v = fathers[v][k];
    		}
    	}
    	return fathers[u][0];
    }
    
    int main(){
      dfs(1, 0);
      //常数优化
      for (int i = 1; i <= n; i++) {
        lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
      }
    }
    

    LCA(树剖)

    int num = 0;
    int head[maxn], depth[maxn], siz[maxn], dad[maxn], top[maxn];
    //top[i]:节点i所在重链的链头
    
    struct Edge {
    	int next, to;
    }edges[2 * maxn];
    
    void add_edge(int u, int v) {
    	num++;
    	edges[num].to = v;
    	edges[num].next = head[u];
    	head[u] = num;
    }
    
    void getDepth(int now) {
    	siz[now] = 1;
    	depth[now] = depth[dad[now]] + 1;
    	for (int i = head[now]; i != 0; i = edges[i].next) {
    		int v = edges[i].to;
    		if (v != dad[now]) {
    			dad[v] = now;
    			getDepth(v);
    			siz[now] += siz[v];
    		}
    	}
    }
    
    void getLink(int x) {
    	int t = 0;
    	if (!top[x]) top[x] = x;
    	for (int i = head[x]; i != 0; i = edges[i].next) {
    		int v = edges[i].to;
    		if (v != dad[x] && siz[v] > siz[t]) t = v;
    	}
    	if (t) {
    		top[t] = top[x];
    		getLink(t);
    	}
    	for (int i = head[x]; i != 0; i = edges[i].next) {
    		int v = edges[i].to;
    		if (v != dad[x] && t != v) getLink(v);
    	}
    }
    
    int LCA(int u, int v) {
    	while (top[u] != top[v]) {
    		if (depth[top[u]] < depth[top[v]]) swap(u, v);
    		u = dad[top[u]];
    	}
    	//直到u和v位于同一条重链上
    	return (depth[u] < depth[v]) ? u : v;
    	//深度更小的那一个就是公共祖先
    }
    
    
    int main()
    {
    	//	ios::sync_with_stdio(false);
    	//	int t; cin >> t; while (t--) {
    	int n, m;
    	scanf("%d", &n);
    	for (int i = 1; i <= n - 1; i++) {
    		int u, v;
    		scanf("%d%d", &u, &v);
    		add_edge(u, v);
    		add_edge(v, u);
    	}
    	getDepth(1);
    	getLink(1);
    	return 0;
    }
    

    康托展开&逆康托展开

    const int FAC[] = { 1,1,2,6,24,120,720,5040,40320,362880,3628800 };
    
    int cantor(int* a) {//算出全排列对应的哈希值
        int x = 0;
        for (int i = 0; i < 9; i++) {
            int smaller = 0;
            for (int j = i + 1; j < 9; j++) {
                if (a[j] < a[i]) smaller++;
            }
            x += FAC[9 - i - 1] * smaller;
        }
        return x+1;
        //注意全排列数组a是从零开始的
    }
    
    
    void decantor(int x,int*ans) {//x哈希值,n数字个数,a算出的全排列
        x--;
        vector<int> v;
        for (int i = 1; i <= 9; i++) v.push_back(i);
        for (int i = 0; i < 9; i++) {
            int r;
            r = x / FAC[9 - i - 1];
            x = x % FAC[9 - i - 1];
            sort(v.begin(), v.end());
            ans[i] = v[r];
            v.erase(v.begin() + r);
        }
        //注意算出的全排列数组ans是从0开始的
    }
    
  • 相关阅读:
    Spring依赖注入的方式、类型、Bean的作用域、自动注入、在Spring配置文件中引入属性文件
    RESTful风格、异常处理、Spring框架
    文件上传、数据校验(后台)、拦截器
    接收的参数为日期类型、controller控制层进行数据保存、进行重定向跳转
    SpringMVC入门Demo
    Mybatis入门Demo(单表的增删改查)
    Spring
    spring的exception
    restful风格
    Java后台验证
  • 原文地址:https://www.cnblogs.com/streamazure/p/13057791.html
Copyright © 2011-2022 走看看