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\n", 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\n",query(1,l,r));
        }
    }
    return 0;
}

 

posted @ 2020-10-28 20:01  SkyFairy  阅读(100)  评论(3编辑  收藏  举报