套路题目

思维技巧

  1. 将树上所有到从 的路径的距离不超过的点的点权加上,单点询问

    再链上,边,距离,为的被距离,为的覆盖,所以只用考虑距离每个点子树内恰好为,继续树上差分,开d颗树状数组,表示给的深度k加上这个值,然后考虑从向上走的贡献,记录表示对子树内距离不超过加的值,再删除,在的时候

    for(ri i=0;i<=20&&x;i++) {
    res+=fen[i].rangequery(in[x],out[x]);
    for(ri j=dis;j<=20;j++)res+=f[x][j];
    ++dis;
    x=father[x];
    }

    即可

  2. 的简单路径上的所有点点权异或和 ()

    表示 的点权, 表示树上 简单路径上所有点的点权异或和。

    等价于 ,然后考虑只修改就可以了

  3. 多次考虑一颗子树的贡献,那么可以转化成一个点对其到根的链上的点的贡献()

  4. 一条边两端的选点问题边与点配对

  5. 树上问题

    参考

  6. 一棵树的直径的中点唯一

  7. 树上联通合并的时候,新的连通块直径只能是个端点中的两个

  8. 求子树外,倍增序列,那么就是连续区间了。

  9. 节点带颜色,考虑动态开点或者总共的算法或数据结构

  10. 树上贪心:一般从叶子节点开始贪心,才能保证最优。

图论

最短路

  1. 两点间最短路径是简单路径,不会有环路 (最长路径)

  2. 求两个环之间长度的差值,正向边,反向边,(假面舞会)

  3.  

图论(模型)转化

  1. 权值最大不计数,权值最小双计数,问最短距离。

    跑分层图

  2.  

优化建图

  1. 有关或者倍数关系的建图,一定要往完全图树的方向想,例如说:两两的连边,那么可以,连边,连通性一定满足,大小为

杂项

  1. 完全图补图

  2. 奇环套奇环不可能生成奇环

  3. “存在一个” 一定要想着转换成 “不存在,对于所有” ()

    本质上是存在性问题转化成限制问题

  4.  

二分图

  1. 二分图小优化:每次如果没有发现一个可行的匹配,那么就不清空 数组

    下一次走到这里还是不匹配

  2. 若一条边一定在最大匹配中,则在最终的残量网络中,这条边一定满流,且这条边的两个顶点一定不在同一个强连通分量中。( 新型城市化)

  3. 无向图是不能用网络流求最小路径覆盖的。

  4. 带权二分图匹配不带权 ()

  5. 并查集判断二分图

    pair<int, int> findf(int x) {
       if (x == f[x])  return make_pair(x, 0);
       pair<int,int> ls = findf(f[x]);
       ls.second ^= g[x];
       return ls;
    }
    bool merge(int x, int y) {
       int op = 1;
       pair<int, int> ls;
       ls = findf(x);  
       op ^= ls.second;
       x = ls.first;
       
       ls = findf(y);
       op ^= ls.second;
       y = ls.first;
     
       if (x == y) return op == 0;
       if (sze[x] < sze[y])
           swap(x, y);
       sze[x] += sze[y];
       f[y] = x;
       g[y] = op;
       del[++cnt] = y;
       return 1;
    }
  6. 二分图最小字典序匹配通解:()

    注意倒着匹配是会出问题的,只有在度数为2的时候才正确。

     

    所以正确的通解是这样的:

    方法2:从前往后枚举,对于每一个点,从小到大假设它与谁匹配,如果它匹配后剩下的图仍能构成

    完美匹配,就说明它可以与当前儿子匹配,删掉这个点与其匹配点。

    方法3:在算法 中,每假设当前点与某个点 匹配,就要对整个图跑一次二分图匹配,但实际上

    大多点在这次匹配操作后是没有影响的。所以我们可以先在开始时对整个图跑一次二分图匹

    配。经过分析,我们可以发现把 匹配只会影响两个点:目前与 匹配的点与目前与

    匹配的点。所以每次只用对影响的这两个点中的任意一个点跑一次增广路就可以了

     

  7.  

生成树

  1. 有向图的最小生成树?按照高度为第一关键字,距离为第二关键字即可 (滑雪)

动态规划DP

参见科技树

序列liner

  1. 一些数,自选操作,问结果的和 ()

    单点贡献法,考虑每一种数,会对哪些结果产生影响

  2. 给一些线段,问点分别被哪些线段覆盖()

    的过程,只会影响右端点在,左端点在的情况,每个线段被修改2次

  3. 对数,,每一对数要么选要么选,问总共选的最大值,)

    先选,再加上堆里恰好个最大的

  4. 两个集合一一对应,无序,要求有序,则可以不管的顺序,求完后再给排序就行。()

  5. 个传送器,分别位于 ,可以从点 传送到 ,并且花费,再整点安装额外传送器使得可以从 传送到 且花费少于 。最小化这个安装数量。()

    表示将长度为 的段分为 段所需的最小费用,有结论:

    ,都有

  6. 最少能删除几个最大能保留多少

  7. 多个数取不同模的时候,可以想想所有模数的LCM,找找性质()

  8. ,想到单调队列,意义在于:找到左边右边第一个大于的数字()

  9.  次询问,每次给出 ,问有多少个  满足 且区间  是好的

    离线下来扫描线,具体的,设当前枚举右端点为 ,那么 里好的区间的数目。这样当 时,直接求当前 ,就是询问 的答案。

  10. 集合内,但无法记录仪当前已经用过的数的状况,考虑连通块)

  11. 给定个数,,每次选定任意个数,最后变成全,那么只要满足,具体可用数学归纳法证明。

  12. 极差最小化? 双指针()

  13. 相互限制问题,可以转化成拓扑排序

  14. 涉及到前缀和计数的题目,往树和子树关系上面去思考

  15. 如果多次询问划开了原来的分组方式,可以考虑每个点可以拓展的区间,再区间求或者取以达到多次询问在线反应的效果 ()

  16. 问一个区间多少种数?HH的项链,而且树状数组大小不是值域!!!是!!! ()

  17. 序列而且两两交换,不好,可以考虑置换群 ()

  18. 快速计算当前连通块个数,每次合并如果成功则,否则不变。()

  19. 若干区间,单点,问哪些区间在哪些时刻达到 ( )

    值域分块,每单点加,将距离的区间加入线段树,在下一个之前单点修改,直到为

  20. 给定序列,区间求连续的段 (讲课)

    1. 统计有多少

    2. 统计有多少,然后所有的个数减去连续的个数

  21. 求第 k 大的距离 (道路の建設案)

    二分答案,然后计算距离 的点对数

  22. 区间加,区间开根号 ()

    记录区间内,开根号时,判断是否等于,如果等于,等价于区间加一个负数值,如果不等直接继续递归

    原因在于开根会导致极差除以,这样每个区间最多会进行次修改,一次区间加,会导致个区间加1e5,所以总复杂度

  23. 集合内点两两之间最大距离,可以考虑按照进制位数次,每次求两个集合之间的最大值 ( 旅行者)

  24. 给定序列,支持序列中元素插入到最后或最前。()

    在最前或最后直接新建若干空位置即可

  25. 维护一个数前面有多少个数。()

    将有数的位置赋值为求前缀和即可

  26. 状压可以考虑记忆化降低复杂度

  27. 本质不同?可以将相同的值加以区分()

  28. 连通块个数=黑点个数减去相邻黑点个数 ()

  29. 多边形内,端点在边界,和切割相关

    想到门槛模型,断环成连,变成区间之间的问题。

  30. 求最大个有序对,等价于求个无序对。

  31. 个集合,每个集合若干个数,取总集合前大,很大

    异或粽子经典做法,每次都选每个集合的最大值,把选了最大值得集合求次大值.....以此类推

  32. 对若干次操作后的集合内元素求和:表示操作后的数的数量和,那么答案就是

  33. 对所有情况求出总和或者每种情况,想差分( )

  34. 子序列问题可以通过证明不交转化成区间问题 ()

  35. 淘汰赛个人,每次个人比赛,那么树高最高是

  36. 给你两个可重集 的元素个数都为 ,它们中每个元素的大小 。请你分别找出 的子集,使得它们中的元素之和相等,试证明对于任意序列都存在解。

    pp1BQmR.png

     

组合数

  1. 逆元预处理

    fact[0]=infact[0]=1;
    for(int i=1;i<=n;i++)
      fact[i]=(ll)i*fact[i-1]%mod;
    infact[n]=quick_mi(fact[n],mod-2);
    for(int i=n-1;i>=1;i--)
      infact[i]=(ll)infact[i+1]*(i+1)%mod;

     

关于2

位运算

1. 只会存在倒数这几位不同 ()

二叉树

  1. 二叉树,线段树,每一层序号连续,层与层序号连续 ()

线性基

  1. 原序列中任意一个数都可以由线性基中的若干个数异或出来

  2. 线行基中的数的个数唯一确定,且数的个数最少

    考虑先插入无法插入,那么插入无法插入

  3. 线性基求最大值,从高到低位贪心

  4. 线性基合并,假如有合并,那么先完全插入,再按照普通插入方式依次插入即可

数据结构

树链剖分

  1. 问一条路径上相同颜色的颜色段数量,边两端颜色相同的边的数量,最好有序()

    维护一个结构体,存左端点颜色,右端点颜色,中间的答案】

    具体写法:

    int range_query(int x,int y){
    bool flg=0;//flg表示当前答案应归到哪边,0为ans1,1为ans2
    Node h,ans1=(Node){0,0,0};
    Node ans2=(Node){0,0,0};
    while(topf[x]!=topf[y]){
    int tx=topf[x],ty=topf[y];
    if(deep[topf[x]]<deep[topf[y]]){
            flg=!flg;
            swp(topf[x],topf[y]);
            swp(x,y);
      }
    //记得同时取反flg
    h=sgtree.query(idx[topf[x]],idx[x],1,n,1);
    //一定注意:每次的查询h,一定是左端点在深度小的地方,
    //右端点在深度大的地方,所以千万不能把左右端点合并错
    if(flg)
       ans2=(Node){
    h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc)
    };
    //ans2情况
    else
    ans1=(Node){
    ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc)
    };
    //ans1情况
    x=fa[topf[x]];
    }
    if(deep[x]<deep[y]){swp(x,y);flg=!flg;}
    h=sgtree.query(idx[y],idx[x],1,n,1);
    if(flg)
       ans2=(Node){
    h.lc,ans2.rc,ans2.cnt+h.cnt+(ans2.lc==h.rc)
    };
    else
    ans1=(Node){
    ans1.lc,h.lc,ans1.cnt+h.cnt+(ans1.rc==h.rc)
    };
    //末处理
    return ans1.cnt+ans2.cnt+(ans1.rc==ans2.lc);
    }
  2. 当遇到边权转点权的时候,不能更新到,具体处理方法如下

    void C(int x,int y,int k){
    int tx=top[x],ty=top[y];
    while(tx!=ty){
        if(dep[tx]<dep[ty]) swap(x,y),swap(tx,ty);
        change(1,1,n,id[tx],id[x],k);
        x=fa[tx],tx=top[x];
    }
    if(x!=y) {
        if(id[x]>id[y]) swap(x,y);
        change(1,1,n,id[x]+1,id[y],k);
    }
    }
  3. 遇到额外边问题,多想想对于连成环的树边的影响()

  4.  

莫队

 

分块

  1. 遇到要区间集体右移左移,可以考虑分块,每一个块维护一个,例如)

  2.  

网络流

建图技巧

  1.  

STL

要注意的

  1. 是支持中间插入的,(函数)

    deque<int>::iterator it=deq.insert(deq.begin()+len,10);
  2. 函数,返回距离左侧个距离的迭代器。

    std::list<int> mylist{ 1,2,3,4,5 };
    std::list<int>::iterator it = mylist.end();
       //获取一个距离 it 迭代器 2 个元素的迭代器,由于 2 为正数,newit 位于 it 左侧
    auto newit = prev(it, 2);
    cout << "prev(it, 2) = " << *newit << endl;
       //n为负数,newit 位于 it 右侧
    it = mylist.begin();
    newit = prev(it, -2);
    cout << "prev(it, -2) = " << *newit;
  3. 离散化板子要记牢

		sort(lsh+1,lsh+n+1);
		cnt=unique(lsh+1,lsh+n+1)-lsh-1;
		//unique指向的是第一个重复的元素,所以剔除重复元素需要-1
		for(int i=1;i<=n;i++)
			num[i]=lower_bound(lsh+1,lsh+cnt+1,num[i])-lsh;
  1.  

 

更高级的应用

二维数点

注意,这里的都是二维平面,不是二维数组,特殊情况会附加说明。

  1. 多次询问,每次询问

    做扫描线,从1开始扫描,往上扫描,每次将与线段树原有点取min,然后当的时候,线段树询问区间的即可

     

  2. 多次询问,每次询问一个矩形内最小的数。

    二位线段树(第二维动态开点)

  3. 个点对,每个点有权值,每次给定区间,求)

    也是普通二位数点,做法:将询问挂在右端点,扫描线,到达右端点后直接区间查询线段树,单点更新以当前点为右端点的点对。

  4. 两棵树,每个点在两棵树上的位置不同,问既在第一棵树的子树内,又在第二棵树的子树内的点的个数 (模拟赛)

    转化成每个点有两个属性,分别表示在第一个树和第二个树上的序,就变成标准长方形数点问题了

    其实也可以不使用二维数点,将在第一棵树的子树的答案差分一下,变成答案减去在扫描到的时候已经存在于第二棵树的子树的点,再加上扫描到的时候存在于子树内的点即可。

     

复杂度

  1. 树上启发式合并(堆)是,

题意转化

  1. 求所有方案的操作数之和,可以考虑对称性()

  2. 序列上立马想到基环树

网格图问题

  1. 给定若干个矩形,然后求每个点被覆盖到的矩形集合中,左边界最靠左的那一个是谁(下同理)

    考虑在加入一个矩形的时候,对于这个矩形的右边界,将所有右边界的值更新为左边界的横坐标值,最后从右往左扫描一遍,每次即可,当然初始化的时候肯定是自己

  2. 网格多米诺问题:状压倒数一行或者倒数一二行。

杂项

平方数相关

  1. 相同的数相乘是平方数,考虑把两个数分别分解成平方数乘常数()

  2. 快速判断是否是平方数:给每一个质因子赋一个随机值,如果一个数的所有质因子的结果是则是平方数

  3. 如果炸了,考虑

约数相关

  1. 涉及到枚举,可以考虑转向枚举的因数,降低复杂度()

  2.  

一些杂题

  • CF1574E

  • CF1633F

  • CF1651E

  • CF1644F

  • CF1697E

  • CF1519E

  • CF1519F

  • CF1469E

  • CF1455E

  • P7561

  • CF1278E

  • CF1487F

特殊题型

格子问题

  • CF1739E

全局计算问题 ()

 

 



posted @ 2023-03-24 10:32  starslight  阅读(112)  评论(0)    收藏  举报