学习日志1

(这是一个监督自己不要颓废的日志,快要被同省巨佬和同市巨佬吊打死了)

PS:NOI WC希望拿个Ag吧,不想打铁,也不想只限于Cu.

该学习日志(2021.1.31 ~ 2021.2.6)

Need to do

  1. MTT学习(完成)
  2. 补[SDOI2017遗忘的集合](完成)
  3. 改模拟赛T1(完成)
  4. 消化几道下午zyy讲的题(已解决3题)
  5. 推导Catalan数生成函数(完成)
  6. 消化部分下午jmr老师讲的题
  7. APIO2019(完成)
  8. SDOI2017(基本完成,但是有3道题需要一些新知识,先咕咕咕,以后再补)
  9. 联合省选2020A卷(除论文题外完成)
  10. HNOI2018
  11. DDP学习

upd 2021.1.31

1.给n个点,m个边的无向图,边可选可不选,求选若干个边使得该无向图为边双联通图的方案数.

  • 首先考虑发现边双连通图很不好做,考虑容斥,用总数减掉不是边双联通图的方案数
  • 然后考虑不是边双联通图是什么情况,(以下为了方遍把孤立点当成边双连通分量),如果把一个极大边双联通分量当成一个大点,那么一定是若干个大点构成的森林,如下图,k表示两个点之间有多少连边,(一个大点实际是一个点集)
  • 我们考虑先枚举划分,把它划分成若干个大点,然后考虑怎么做,设F[S]为S个大点构成树或森林(即不是双连通分量)的方案数,考虑枚举叶子节点T去掉,即F[S] = F[S-T] * (G[T,S-T]) * $(-1)^{|T|-1}$,考虑为何要奇加偶减,因为我们考虑事实上我们枚举的每个叶子集合都会被它的非空子集计算一次,而奇子集 = 偶子集 = 偶非空子集 + 1(空集为偶集),G[T,S-T]为T到S-T的连边方案数.
  • 然后我们考虑不划分,直接设H[S-T,T,0/1]为集合T被分成了奇数个/偶数个双连通分量对集合S-T的贡献,F[S]为S这个集合不是双连通分量的方案数,考虑F[S]仍是由若干个大点构成的森林, F[S] = $\Sigma$F[S-T] * (H(S-T,T,0) - H(S-T,T,1)),H[S-T,T,o] = $\Sigma$H[S-T,P,o^1] * (A - F[P]) * G[P,S-T].(P是T的子集,A是P任意连边的方案数,要求P包含T编号最小的点).Time Complexity O(4 ^ n)
  • 这里说一个trick:什么要强制xx包含xx编号最小的点以避免算重,当该集合内的点都没有特征没法分辨的时候.(但比如对于树或DAG来说就有一些有特征的点,如叶子,入度为0的点,所以就不用用这个trick)
  • 此做法可方便地拓展到有向图强连通分量方案上

upd 2021.2.01

  ICPC/CCPC题目选讲(抄(参)袭(考)自zyy课件)

  1.K. Traveling Merchant(ICPC Shang hai 2020)

   考虑解一定是一个1->x->y->x,其中1->x->y都是0-1边,y->x是同色边(此时x因为被遍历一遍已经变成和y异色),于是我们可以考虑只对异色边建图,并构建一颗根为1的圆方树,1->x,x->y之间的边不能有重叠,所以对于每个同色边(x,y)有解只有以下两种情况

  • x是y的祖先,那么直接从1沿树边走到x,再走到y
  • 1走到x然后通过x的点双,在环上绕一下(但不越过1走到x的点)走到y,反映在圆方树上就是y在x父亲所在方点的子树内.

  建完圆方树预处理对每个边都判一下即可

 

  2.H. Rikka with Storehouse (2020 XiaoMi Invitational Programming contest)

  归纳可以发现对于一个非叶子节点,设其权值为t,f(t)是一个二次函数,怎么归纳及求二次函数的系数呢,首先如果两个儿子都是叶子,设为x,y,那f(t) = $(t - x)^{2}$ + $(t - y)^{2}$,否则我们考虑把两个子树一个个加进去,例如仍设左儿子为x,把x加进去时有,f(t) = a*x^2 + b*x + c + (x - t)^2 = (a + 1)*x^2 + (b - 2 * t) * x + C,C是和x无关的常数,由二次函数极值点带进去发现结果仍是一个关于t的二次函数,那么每次修改都重新更新一遍二次函数的系数即可.(怎么想到的呢?也许是看到二次的形式开始猜想??不是很懂)

 

  3.E. Tree Paths(Petrozavodsk Winter Training Camp 2018)

考虑这种问题一般有两个角度,要么考虑树链,然后判断计算连续段,要么从左到右扫描线连续段,然后判断是否是树链.这道题两种角度考虑都可以,但第二种比较清晰和简单

  • 首先我们考虑用线段树维护左端点,然后扫描右端点,设当前右端点为r,对于每个左端点维护从当前的右端点到它的点数-边数,(如果=1说明联通),以及=1的个数,只要每新添一个点往前面区间+1,枚举边往前面-1即可.
  • 我们还需对于维护极大的[l',r]表示极大的区间[l',r]为广义链,这里定义广义链为其点集的虚树是链,可以发现两个重要性质,1.联通的广义链即为树链,2.广义链的子集仍是广义链.我们维护广义链在树上的左端点,右端点(不是数值的左端点右端点,是链的),以及是直上直下的链或者不是,合并的时候分类讨论一下就可以了(对每个区间维护紧靠右边的极大广义链,对于每个区间,若右子区间满了就合并左边,否则就是只有右边的答案)
  • 设当前广义链为[l,r],计算[l,r]区间内点数-边数=1的个数即可

 

  

  upd 2021.2.2

国家集训队作业选做(参(抄)考(袭)自jmr老师的ppt)

  1.[ACG023F] 01 on Tree

  • 首先容易发现在删掉某个点之后,对于儿子,一定是先选0,后选1,0可以一下子全选后,但1选的先后顺序我们不确定
  • 于是问题就转化成每个点上面有一个01序列,求怎么选使得逆序对最小
  • 设i点0个数为a(i),1个数为b(i),对于排列P,考虑临项交换,对于$P_i$,$P_{i+1}$,$P_{i}$排前面当且仅当,b($P_{i}$) * a($P_{i+1}$) <  b($P_{i+1}$) * a($P_{i}$),于是按照$\frac{b(P_i)}{a(P_i)}$排序,每次取除根以外最小的合并到父亲上,(分母=0视为无穷大),边合并边累加逆序对,用并查集维护,合并到只剩根节点时即为答案.
  • Tag:贪心,临项交换

   2.[AGC034E] Complete Compress

  • 想错了好几次,讲一下错误,就是每次只单纯地利用子树处理的结果来贪心.错误原因:不同子树之间会互相影响.启示:要分成多个互不相关的子问题前,问问自己子问题之间是否互相影响.
  • 考虑枚举最终停止的点,以它为根,设为rt,设当前在处理u的子树,g(v)数组表示当前时刻v子树的点到u的距离(实际只用到u和u的儿子,所以只要更新它们即可),如果我们把每走一步都看成移掉一个点,那么就可以把一个点拆成到u距离那么多个点
  • 于是转换成经典模型,考虑u最大的儿子maxp,设f[u]数组表示u的子树最多能消掉多少对,若g[maxp] * 2 <= g[u],则可以全部消掉(如果是奇数会留一个),否则我们将maxp之外的全部跟maxp内的匹配,然后再考虑maxp这个子树能消掉多少,显然为min(f[maxp],(和外面匹配完还剩的点) / 2)
  • 核心代码如下
    void dfs(int u,int fa){
        sum[u] = c[u];
        int maxp = 0,S = 0;
        for(int i = head[u]; i ; i = edge[i].nxt){
            int v = edge[i].point;
            if(v == fa)    continue;
            dep[v] = dep[u] + 1;
            dfs(v,u);g[v] += sum[v];
            if(!maxp || g[maxp] < g[v])    maxp = v;S += g[v];
            g[u] += g[v];sum[u] += sum[v];
        }
        int res = S - g[maxp];
        if(g[maxp] <= res)    f[u] = g[u] / 2;
        else    f[u] = g[u] - g[maxp] + min(f[maxp],(g[maxp] - (g[u] - g[maxp])) / 2);
    }
    void solve(int rt){
        memset(f,0,sizeof(f));memset(g,0,sizeof(g));
        dep[rt] = 0;
        dfs(rt,0);
        if(f[rt] * 2 >= g[rt]){
            ans = min(ans,g[rt] >> 1);
        }
    }
  • Tag: DP,贪心

  upd 2021.2.3

  1.[APIO2019]路灯(看了题解)

  • 把路灯看成边,问题等价于a,b两点联通的时间,首先可以考虑把用一个矩阵维护任意两点[a,b]联通的时间(没想到,一直在想一维的情况所以想不出来)
  • 用另一个线段树维护连通性,考虑联通时减掉时刻j,断开时加上时刻i,那么存储的答案刚好是i-j即联通的时间,联通/断开[l,x],[x+1,r]等价于对[l,x+1] ~ [x,r]的矩阵区间修改,查询等价于单点查询
  • 树套树维护即可.
  • 启示:数据结构题,如果涉及多维有时候可以拓展一维放到平面上思考(也不一定要放时间维,像此题,两个点都是平面上的点也可以转化成是否联通的二维数点)
  • Tag:树套树,数据结构

  2.[APIO2019]桥梁(主要自己想,但有细节瞟了题解)

  • 发现如果没有修改,我们可以从大到小加边,离线用并查集维护集合大小来做.修改O(m),查询O(1)
  • 发现我们还可以暴力地每次每次查询时,把前面修改过的边合法的全部加进并查集,修改O(1),查询O(m)
  • 于是我们考虑对操作进行分块,设块长为Siz,块的多少即为Q/Siz,对于每个块内,每次对于没有修改的边从大到小加入,O((Q/Siz) * mlogm),然后对于修改过的边(显然不超过Siz),对于每个询问暴力加入并查集内,询问完撤销,O(Siz*QlogQ),经典的根号分治.
  • 启示:如果我们有一个修改O(m),查询O(1)的算法,又有一个修改O(1),查询O(m)的算法,常常可以根号平衡,即把操作分成若干个块内,块内对于没修改的预处理,修改过(数量<=块长)的暴力加入.
  • Tag:根号分治,分块,数据结构

  3.[APIO2019]奇怪装置(自己做出)

  简单的同余方程+线段树,挺simple的,会做所以就不写了

  upd 2021.2.4

  1.联合省选2020D1T2,简单推导没什么好说的,有一点就是Stir[0][0]要置为1,因为$0^0$ = 1

  2.联合省选2020D2T3,额,和上题一样是个简单的数学推导,记住martix tree theory 是度数矩阵 - 邻接矩阵就行(貌似反着来也能对?不懂).

  3.[SDOI2017天才黑客](看了题解)

  • 因为权值和边与边之间有关系,所以考虑把边拆成如点和出点,中间连这条边的权值
  • 考虑边拆成的点(以下简称点,原图中的点称原点)之间有连边当且仅当有原点作为中转,且边权为它们上面字符串的lcp,考虑字符串的lcp即字典树(题目给出)上的lca的深度,即本质不同的lcp只有k个,意即我们建的很多边都是没用的.怎么办呢?线段树优化建边!
  • 然后接下来有个问题:枚举字典树上lca时,怎么知道它们在原图中能不能有中转点能不能互相到达.(反正我想到这里就卡住了)
  • 一个办法是,对于每个原点,每次都只加入原点上的边对应的在字典树上的点,构建虚树,这样就能保证加进来的出点能够到达加进来的每个入点,在虚树上枚举lca优化建图即可
  • 初始化就是建个超级源,对每个形如(1,a)的边的入点连一个边权0的边
  • 细节很多,我写了8kb
  • 启示:如果发现对边与边之间的限制/关联大与点与点之间的,常常可以考虑拆点,在想如果只想保留某些关键点时,常常可以考虑虚树
  • Tag:虚树,线段树优化建图
posted @ 2021-01-31 22:42  y_dove  阅读(179)  评论(4编辑  收藏  举报