zoukankan      html  css  js  c++  java
  • 10.28记

    好,today is over!

     

    梳理一下学长讲的

    1.求解LCA

    首先是求LCA,有倍增法,树链剖分,欧拉序都可以求解LCA(码风我看着很舒服,整齐就完事了

    倍增法:

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define N 500000
    #define M 500000
    using namespace std;
    const int mod = 1e9+7;
    const int inf = 0x3f3f3f3f;
    inline int read() 
    {
      char c = getchar(); int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    int n,m,s;
    //======================================
    int f[N][30],dep[N];
    struct node
    {
      int nxt,to;
    }edge[M<<1];//好几次,都是70分,直到学长让我看看范围,然后问我,树是无向图,你为什么不建双向边,题目给定是一颗树,给定的数据又非节点到他的儿子;
    int number_edge,head[N];
    void add_edge(int u,int v)//经典存图
    {
      number_edge++;
      edge[number_edge].nxt=head[u];
      edge[number_edge].to=v;
      head[u]=number_edge;
    }
    void dfs(int u,int fa)//这个倍增实现的就是求深度,和找父亲节点
    {
      f[u][0]=fa;
      dep[u]=dep[fa]+1;
      for(int i=1;(1<<i)<=dep[u];i++)
      {
        f[u][i]=f[f[u][i-1]][i-1];
      }
      for(int i=head[u];i;i=edge[i].nxt)
      {
        if(edge[i].to!=fa)
        {
          dfs(edge[i].to,u);
        }
      }
    }
    int lca(int u,int v)//求解LCA
    {
      if(dep[u]>dep[v])
      {
        swap(u,v);
      }
      for(int i=15;i>=0;i--)
      {
        if(dep[u]<=dep[v]-(1<<i))
        {
          v=f[v][i];
        }
      }
      if(u==v)
      {
        return u;
      }
      for(int i=15;i>=0;i--)
      {
        if(f[u][i]==f[v][i])
        {
          continue;
        }
        else
        {
          u=f[u][i];
          v=f[v][i];
        }
      }
      return f[v][0];
    }
    int main()
    {
        n=read(),m=read(),s=read();
        for(int i=1;i<=n-1;i++)
        {
          int x=read(),y=read();
          add_edge(x,y);
          add_edge(y,x);
        }
        dfs(s,0);
        for(int i=1;i<=m;i++)
        {
          int u=read(),v=read();
          cout<<lca(u,v)<<endl;
        } 
        return 0;
    }

    树链剖分:

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #include <queue> 
    #include <cmath>
    using namespace std;
    const int maxn=500010;
    inline int read() 
    {
      char c = getchar(); int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    //==============================================
    struct node
    {
        int nxt,to;
    };
    node edge[maxn<<1];
    int n,m,root,number_edge;
    int size[maxn],son[maxn],f[maxn],dep[maxn],head[maxn],top[maxn]; 
    //==============================================
    void add_edge(int from,int to)
    {
        number_edge++;
        edge[number_edge].nxt=head[from];
        edge[number_edge].to=to;
        head[from]=number_edge;
    }
    void dfs1(int now,int fath,int depth)//电风扇1(dfs1) 用来计算重儿子,父亲节点,和深度
    {
        dep[now]=depth;
        f[now]=fath;
        size[now]=1;
        for(int i=head[now];i;i=edge[i].nxt)
        {
            if(edge[i].to==fath)
            {
                continue;
            }
            dfs1(edge[i].to,now,depth+1);
            size[now]+=size[edge[i].to];
            if(size[edge[i].to]>size[son[now]])
            {
                son[now]=edge[i].to;
            }
        }
    } 
    void  dfs2(int now,int top_)//求顶端,划分链为重链还是轻链
    {
        top[now]=top_;
        if(son[now]==0)
        {
            return ;
        }
        dfs2(son[now],top_);
        for(int i=head[now];i;i=edge[i].nxt)
        {
            if(edge[i].to==f[now]||edge[i].to==son[now])
            {
                continue;
            }
            dfs2(edge[i].to,edge[i].to);
        }
    }
    int lca(int a,int b)//求解LCA
    {
        while(top[a]!=top[b])
        {
            if(dep[top[a]]<dep[top[b]])
            {
                swap(a,b);
            }
            a=f[top[a]];
        }
        return dep[a]<=dep[b]? a:b;
    }
    int main()
    {
        n=read(),m=read(),root=read();
        for(int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            add_edge(x,y);
            add_edge(y,x);
        }
        dfs1(root,0,1);
        dfs2(root,root);
        while(m--)
        {
            int a=read(),b=read();
            cout<<lca(a,b)<<endl;
        }
        return 0;
    } 

     3.欧拉序求LCA

    想法就是

      先求出LCA,然后找到节点 x,y的区间,即[x,y]就这个,找里面深度最浅的点就是LCA(以前没写过,很快,但是很鸡肋,除了求解LCA,貌似没有别的什么用处)

    这很明显,不是我的码风,对,就是学长的)  

    #include <cstdio>
    #include <cctype>
    #include <algorithm>
    #include <cmath>
    #define ll long long
    #define max std::max
    const int MARX = 1e5 + 10;
    //===========================================================
    int N, M, Log2[MARX], MAX[MARX][25];
    int node[MARX][25];
    //===========================================================
    inline int read()
    {
        int w = 0, f = 1; char ch = getchar();
        for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
        for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
        return f * w;
    }
    void Prepare()
    {
      //MAX[i][j]: [i,i+2^j] max_num
        N = read(), M = read();
        for(int i = 1; i <= N; i ++) {
          MAX[i][0] = dep[i];
        node[i][0] = i;
      }
        
        for(int i = 2; i <= N; i ++) {
          Log2[i] = Log2[i >> 1] + 1;
      } 
        for(int i = 1; i <= 20; i ++)
          for(int j = 1; j + (1 << i) - 1 <= N; j ++) {
            if (MAX[j][i] < MAX[j + (1 << (i - 1))][i - 1]) {
               MAX[j][i] = MAX[j][i - 1];   
               node[j][i] = node[j][i - 1];
          } else {
            MAX[j][i] = MAX[j + (1 << (i - 1))][i - 1];   
               node[j][i] = node[j + (1 << (i - 1))][i - 1];
          }
        }
    }
    int Query(int l, int r)
    {
        int Log = Log2[r - l + 1];
        if (MAX[l][Log] < MAX[r - (1 << Log) + 1][Log]) return node[l][Log];
        return node[r - (1 << Log) + 1][Log];
    }
    //===========================================================
    int main()
    {
      printf("%lf", log(500000));
        Prepare();
        while(M --)
        {
          int l = read(), r =read();
          printf("%d
    ", Query(l, r));
        }
        return 0;
     }

     2. 然后口胡某类DP

    3.二进制

    nice,我想了一下,还是就在这个博客里说了吧

    计算机的存储应用的就是二进制,所以二进制这类基础知识就让人不得不去学习,学完之后,你发现,你可以手玩状态压缩DP了,毕竟(我是傻逼

    计算机的储存,由大到小 GB,MB,KB,bit    ,进位就是1024,所以,1GB就是1024^1024^1024 bit

    嗯,然后洛谷(SP2916 时间限制为132ms,然后空间限制 1.46GB,啊这)

    变量是内存中开辟的,一段用于储存数据的空间。

    一个 int,32 个比特,32 个 2 进制位。

    一个 char,8 个比特,8 个 2 进制位。

    补码

    没法表示负数怎么办啊?

    一个变量的第一个 2 进制位设为符号位,第一个 2 进制位为 0 表示正数,为 1 则表示负数。这样的表示法称为补码。

    为保证两个互为相反数的变量相加后为 0,正数的补码即其二进制表示,负数的补码为对应正数各 2 进制位取反后 + 1

    (https://netdisk-pan.baidupcs.com/disk/docview?bdstoken=ff7e264e3f2b4a69321eb08779ac6caa&uk=3848857727&path=%2F%E8%AF%BE%E4%BB%B6%2F%E4%BA%8C%E8%BF%9B%E5%88%B6.pptx&share=0)

    然后就是C++中的一些符号

    每一位独立,除左右移运算,在二进制表示下不进位。

    异或可以看做二进制不进位加法。

    优先级

    还有就是位运算的优先级最低,

    如果是 left+right>>1,表示的就是(left+right)/2,而不是 left+right/2;

    典例:

    1.快速幂

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long quickpow(int x,int y)
    {
        long long ans=1;
        while(y)
        {
            if(y & 1)
            {    
            ans*=x;
            y>>=1;
            }
            x=x*x;
        }
        return ans;
    }
    int main()
    {
        int x,y;
        cin>>x>>y;//x^y 
        cout<<quickpow(x,y);
        return 0;
    } 

    2.64 位整数乘法

      求 x*y%mod ,x,y,mod<=10^18 

    直接相乘直接炸掉long long  ,因为   x(a+b)=x*a+x*b(小学数学,乘法分配率),讲x,y拆分成二进制,从而过掉 10^18,复杂度从O(1)变成了 O (log)  (by the way,C++自带 log函数)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long quickpow(long long x,long long y,long long mod)
    {
        long long ans=0;
        for(;y;y>>=1)
        {
            if(y & 1)
            {    
            ans=( ans + x ) % mod; 
            }
            x = (x + x) %mod;
        }
        return ans;
    }
    int main()
    {
        long long x,y,mod;
        cin>>x>>y>>mod;
        cout<<quickpow(x,y,mod);
        return 0;
    } 

    4.数据结构

    1.树状数组

    2.线段树

    3.分块:

    本质是暴力分治。

    通过对原数据的适当划分,并在划分后的每一个块上预处理部分信息,从而较一般的暴力算法取得更优的时间复杂度。

    可以处理并维护许多前两者无法维护的复杂信息,如区间众数。 空间换时间,可看做只有两层的线段树。

    4.一些其他题目

    题单:

    1.ACL Beginner Contest D

    2.SP2713 GSS4

    3.P1438 无聊的数列

    4.POJ 2777 Count Color

    5.P4054 [JSOI2009]计数问题

    6.P2574 XOR的艺术

    7.ACL Beginner Contest E

    8.SP2916 GSS5

    9.Loj2211. 「SCOI2014」方伯伯的玉米田

    10.P2572 [SCOI2010]序列操作

    选做:

    P3797 妖梦斩木棒:线段树模拟。

    AcWing246. 区间最大公约数:区间加,区间 gcd,更相减损。

    P4198 楼房重建 :动态维护单调栈,不寻常的 pushup。

    CF703D:区间出现过偶数次的权值的异或和,扫描线。

    「联合省选 2020 A | B」冰火战士:树状数组上二分。

    P4137 Rmq Problem / mex:区间 mex,扫描线。

    CF407E:线段树+单调栈。

    「TJOI / HEOI2016」排序:强加单调性 + 01序列排序。

    应学长要求众所周知,学长是很欠揍的

    1.P2574 XOR的艺术

       WA掉的的地方对于求解一个区间内1的数目,所求的公式就是 区间长度减去自身的值,忘记+1了,            QwQ    ;

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #include <queue>
    using namespace std;
    const int maxn=2e5+10;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
        return x*f;
    }
    int n,m;
    int num[maxn<<1];
    struct Tree
    {
        int sum,left,right,over;
    };
    Tree tree[maxn<<2];
    void build(int now,int left,int right)
    {
        tree[now].left=left;
        tree[now].right=right;
        if(left==right)
        {
            tree[now].sum=num[left];
            return;
        }
        else 
        {
            int mid=(left+right)>>1;
            build(now*2,left,mid);
            build(now*2+1,mid+1,right);
            
        }
        tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
    }
    void pushdown(int now)
    {
        if(tree[now].over)
        {
            tree[now<<1].sum=tree[now<<1].right-tree[now<<1].left+1-tree[now<<1].sum;
            tree[now<<1|1].sum=tree[now<<1|1].right-tree[now<<1|1].left+1-tree[now<<1|1].sum;
            
            tree[now<<1].over^=1;
            tree[now<<1|1].over^=1;
            
            tree[now].over=0;
        }
        return ;
    } 
    int query(int now,int left,int right)
    {
        if(tree[now].right<=right && tree[now].left>=left)
        {
            return tree[now].sum;
        }
        else 
        {
            pushdown(now);
            int mid=(tree[now].right+tree[now].left)>>1;
            int ans=0;
            if(left<=mid)
            {
                ans+=query(now<<1,left,right);
            }
            if(mid<right)
            {
                ans+=query(now<<1|1,left,right);
            }
            tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
            return ans;
        }
    }
    void change(int now,int left,int right)
    {
        if(tree[now].left>=left &&tree[now].right<=right)
        {
            tree[now].sum=tree[now].right-tree[now].left+1-tree[now].sum;
            tree[now].over^=1;
            return ;
        }
        else 
        {
            pushdown(now);
            int mid=(tree[now].left+tree[now].right)>>1;
            if(left<=mid)
            {
                change(now<<1,left,right);
            }
            if(mid<right)
            {
                change((now<<1)|1,left,right);
            }
            tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
        }
        
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            scanf("%1d",&num[i]);
        }
        build(1,1,n);
        for(int i=1;i<=m;i++)
        {
            int op;
            cin>>op;
            if(op==0)
            {
                int l,r;
                cin>>l>>r;
                change(1,l,r);
            }
            else if(op==1)
            {
                int l,r;
                cin>>l>>r;
                printf("%d
    ",query(1,l,r));
            }
        }
        return 0;
    }
  • 相关阅读:
    MEAN: AngularJS + NodeJS的REST API开发教程
    什么是MEAN全堆栈javascript开发框架
    fputcsv 导出excel,解决内存、性能、乱码、科学计数法问题
    React 高德地图画点画区域放大缩小
    React 拖动布局
    React+TypeScript搭建项目
    js 运算符优先级
    for...in 与 for...of
    前端面试点记录
    Vue 高德地图 路径规划 画点
  • 原文地址:https://www.cnblogs.com/Zmonarch/p/13893096.html
Copyright © 2011-2022 走看看