• 省选做题总结


    省选做题总结

    2.16 - 2.22

    Luogu P4068 [SDOI2016]数字配对

    题目链接

    ​ 定义(cnt[i])(a[i])唯一分解之后所有质因子的指数之和。

    ​ 我们发现当且仅当:(cnt[j] = cnt[i] + 1 &&a[j]\%a[i] = 0)时,(a[i])(a[j])才可以配对。

    ​ 那么如何获得最大价值呢?我们考虑费用流。

    ​ 首先把(cnt[i])为奇数的放左边, 为偶数的放右边,因为可以配对的两个数字他们的(cnt)奇偶性一定不同。

    ​ 然后源点向左部连一条流量为(b[i]),费用为0的边。可以配对的两个节点连一条流量为(inf),费用为(c[i] * c[j])的边。右部向汇点连一条流量为(b[j]),费用为0的边。跑最大费用最大流就好了。

    Luogu P3973 [TJOI2015]线性代数

    题目链接

    ​ 题中给定了这个式子 : (D = (A * B - C) *A^T), 我们考虑把它变换一下形式;

    (D = A * B * A^T -C*A^T = sum_{i = 1}^n sum_{j = 1}^na[i]*b[i][j] *a[j] - sum_{i = 1}^nc[i] *a[i])

    ​ 变换形式后的式子看起来顺眼多了,很容易观察到,(a[i])(a[j])同时取1的时候答案将会加上(b[i][j] - c[i]).

    ​ 所以我们可以考虑最小割。源点向每个(i)连一条流量为(c[i])的边,然后(a[i])(a[j])同时向一个新建节点(k)连一条流量为(inf)的边,然后(k)向汇点连一条流量为(b[i][j])的边,跑最小割就好了。

    ​ 割掉一条从源点出发的边的意义是 : (a[i])取1;

    ​ 割掉一条连向汇点的边的意义是 : (a[i],a[j])取0。

    Luogu P4055 [JSOI2009]游戏

    题目链接

    ​ 首先我们可以把所有的可以相互到达的'.'连一条边,然后求出这个图的最大匹配(k)。一条最大匹配上的边的意义是 :这两个节点是可以相互推的。(废话)

    ​ 如果说(k * 2 = tot)(tot)为总点数,那么说明无论先手走到哪个点,都一定有下一个点可以让后手推,所以先手必败。

    ​ 相反,如果有非匹配点,那么先手放到这些点就可以必胜。因为一个非匹配点一定连着一个匹配点(如果是两个非匹配点相连的话那就又会多一条匹配,与最大匹配矛盾),这样先手只要放了非匹配点,那么后手就只能推向一个匹配点。这样问题就和第一种情况一样了:不论后手推到哪个点,都一定有下一个点可以让先手推,所以先手必胜。

    ​ 我们跑一边匈牙利,求非匹配点(x),但这并不是全部的答案。我们发现二分图上与(x)同侧的可以被(x)取代的那些点也是答案点。比如与(x)相连的点为(y),可以知道(mat[z] = y)的点(z),这个(z)就可以被(x)取代。(dfs)一下就好了。

    Luogu P1368 【模板】最小表示法

    题目链接

    ​ 就是给一个环,问这个环从哪个点断开后出现的序列字典序最小,可能会有相同元素。

    ​ 这是最小表示法的模板题,但我并没有学最小表示法(之后补上吧),所以用了比较麻烦并且有点慢的SA过了这道题。

    ​ 首先我们将这个环从1断开,然后补成原来的二倍,这样比较方便,然后对这个新字符串求一下后缀数组。

    ​ 然后预处理一下(height)数组的区间最小值,这样可以(O(1))的求出任意两个后缀的(lcp)(O(n))的遍历这个字符串,比较答案串和当前串的(lcp)的下一个字符就可以更新答案了。

    Luogu P4171 [JSOI2010] 满汉全席

    题目链接

    ​ 可以说是2-SAT的模板题了。

    ​ 我们可以把一种食材的汉族做法和满族做法分别看成0和1。对于一个评委的要求(i_0,j_1),如果说没有(i)的汉族做法就一定要有(j)的满族做法,没有(j)的满族做法就一定要有(i)的汉族做法。对应的连边就是(i' -> j', j -> i),就是说如果有(i)的满族做法,那么一定要有(j)的满族做法。

    ​ 然后Tarjan判断一下有无解就好了。

    Luogu P5782 [POI2001] 和平委员会

    题目链接

    ​ HDU 1814是这道题的加强版,要求输出所有合法答案的字典序最小的那一个,好像是要(dfs)实现,我还不会。

    ​ 我们可以把一个党派的两个人分别看做0和1。如果有(i,j)不友好,那么连边就是:(i -> j',j ->i')。用Tarjan判断有无解。

    ​ 那么怎么输出合法方案呢?对于同一个党派的两个人(i, i'),谁所在的强联通分量编号小就输出谁。因为每个强联通分量的标号就是反向的拓扑序。

    Luogu P3209 [HNOI2010] 平面图判定

    题目链接

    ​ 首先平面图有一个性质 :(m <= 3 * n - 6)。不满足这个性质的一定不是平面图。

    ​ 所以(m)的数据范围一下子缩小了好多。

    ​ 我们把这个图中的不是哈密顿环上的边都找出来,每条边只有两种可能,在环内和在环外。这就和2-SAT问题的0和1很像了。然后我们在找到可能会相交的那些边,把这两条边看做是矛盾的,就是一个在环内一个在环外,跑Tarjan判断有无解就好了。

    Luogu P4036 [JSOI2008]火星人

    题目链接

    ​ 很明显,平衡树维护哈希值就好了。

    ​ 我们在执行询问操作的时候,可以二分一下(lcp)的长度,然后用平衡树查询一下那段区间的哈希值一样不一样就好了。

    Luogu P4287 [SHOI2011]双倍回文

    题目链接

    ​ Manachar的好题。

    ​ 首先我们把Manachar的板子打上,然后对更新(mx)那里面进行一些操作。们只需要判断更新的这整个回文串的一半是不是回文串就好了。看代码应该更好理解 :

    for(int i = 1;i <= n; i++) {
    		if(mx < i) r[i] = 1;
    		else r[i] = min(mx - i, r[2 * p - i]);
    		while(s[i + r[i]] == s[i - r[i]]) r[i] ++;
    		if(i + r[i] - 1 > mx) {
    			if(!(i & 1)) 
    				for(int j = max(mx, i + 4);j < i + r[i]; j++) {
    					if(!((j - i) & 3) && r[i - (j - i) / 2] > (j - i) / 2) ans = max(ans, j - i);
    				}
    			mx = i + r[i] - 1; p = i;
    		}
    	}
    

    ​ 我们可以知道答案一定是4的倍数。(i - (j - i)/2)是整个回文串前半部分的中点,只要这个点的回文半径大于整个回文串的四分之一,那么整个回文串一定是双倍回文。

    考试题 划分序列

    ​ 给定一个长的为(n)的序列,要求分成(k)段,每一段都是连续的一部分,要求权值最大的子段权值和最小。序列元素为整数。(n <= 50000)

    ​ 如果说所有元素都是整数还比较好说,直接二分就好了。

    ​ 但这道题有负数。还是考虑二分。二分一个(mid)表示权值最大子段的权值和。

    ​ 为了满足这个限制,我们需要把整个序列分成若干段,但是一定会有一个最小段数(L)和最大段数(R).只要(L <= k <= R),那么这个(mid)就是合法的。

    ​ 设(f[i])为前(i)个数字分成合法的段的最小段数。转移方程就是 :

    (f[i] = min{f[j] + 1mid sum[i]-sum[j] <= mid})

    (sum)代表前缀和。我们只需用树状数组优化一下就好了。最后(L)就等于(f[n])

    ​ 求(R)的方法一样,只需设(f[i])为前(i)个数字分成合法的段最大的段,转移改为(max)就好了。

    ​ 时间复杂度(O(nlog^2n))

    bzoj 2223. [Coci 2009]PATULJCI

    题目链接

    ​ 主席树维护(size)就好了,主要是怎么找大于一半的数字。

    int Query(int R, int L, int l, int r, int k) {
    	if(t[R].siz - t[L].siz <= k) return 0;
    	if(l == r) return l;
    	if(t[ls(R)].siz - t[ls(L)].siz > k) return Query(ls(R), ls(L), l, mid, k);
    	else return Query(rs(R), rs(L), mid + 1, r, k);
    }
    

    ​ 这么找就好了。

    Luogu P4219 [BJOI2014]大融合

    题目链接

    ​ 平衡树维护虚链信息。

    ​ 平常维护(size)是这样维护的:

    void up(int o) {
    	siz[o] = siz[ls(o)] + siz[rs(o)] + cnt[o];
    }
    

    ​ 现在要把虚链所连的那些点的(size)也维护上:

    void up(int o) {
        siz[o] = siz[ls(o)] + siz[rs(o)] + cnt[o] + sizx[o];
    }
    

    (sizx[o])代表节点(o)虚链所连的子树的(siz)。所以我们需要在连虚链的地方维护这个(sizx)。就是下面这两种情况:

    (Link)连边的时候连的是虚边;

    (Access)的时候会有虚边的消失和产生。

    Luogu P4172 [WC2006]水管局长

    题目链接

    ​ 平衡树维护生成树。

    ​ 我们发现每删除一条水管可能需要重构最小生成树。不好弄,我们把操作从后向前进行,改为加边。

    ​ 每往生成树中加一条边就会构成一个环,我们在环上找到权值最大的一条边再删掉就好了。

    ​ 连边,删边,找最大值在平衡树上很容易实现。但是还有个问题,就是平衡树上只能维护点的信息,不能维护边。我们可以将边转化成有价值的点,举个例子:当前有一条边的边权为5,连接着(x, y);那我们把他转化成(x,y,z)三个点,((x,z)(z,y))两条边,其中(z)的权值是5。

    CF525D Arthur and Walls

    题目链接

    ​ 构造搜索题。

    ​ 我们用一个2*2的网格遍历整个矩形,这个网格里无非就四种情况:有1, 2, 3, 4个$ * (。我们发现,有2, 3, 4个) * (的都不用改,只有1个) * (的时候需要改,因为不管这个2*2的网格覆盖在在矩形的任何方位,都不可能出现仅有一个) * $的情况。所以我们只需搜索这种情况就好了。

    Luogu P6247 [SDOI2012]最近最远点对

    题目链接

    ​ 这本来是K-D树的题,但我不太会,于是看到一种乱搞的方法感觉挺好的。

    ​ 随机旋转坐标系角度,然后对所有点进行以(x)为第一关键字,(y)为第二关键字排序,然后暴力枚举部分节点统计答案就好了。正确率非常高。

    ​ 有一个旋转公式需要记住,点((x, y))逆时针旋转(alpha)度后:

    ((cos alpha*x - sinalpha*y,sinalpha*x+cosalpha*y))

    CF103D Time to Raid Cowavans

    题目链接

    ​ 根号分治。(这种题见得挺少的)

    ​ 对于(n/k)比较小的,我们直接暴力跳块就好了。

    ​ 其余的我们预处理一下后缀和就好了。

    ​ 但是所有都预处理空间会炸。我们考虑把所有操作都离线下来,把(k)相同的都存在一个vector中。每次只预处理一个(k),空间就没问题了。

    P3201 [HNOI2009] 梦幻布丁

    题目链接

    ​ 对于修改,我们是要合并两个集合,显然要启发式合并。

    ​ 其实知道要用启发式合并就没什么好说的了,实现还挺简单的。

    ​ 简单说一下启发式合并的复杂度 : 我们每次将小的集合合并到大的集合上面,显然小的集合的大小会翻倍。点数上限是(n),所以对于每个点,我们最多就会合并(logn)次,所以总时间复杂度是(O(nlogn))的。

    CF600E Lomsat gelral

    题目链接

    ​ 仍然是启发式合并。

    ​ 我们考虑统计一颗树中的颜色(根为(x)),是不是先要统计它每一棵子树中点的颜色,那是不是最后一棵子树统计完了颜色的出现次数不用清0,直接用到当前树(x)中去就好了。所以为了使用重复统计的节点数量最多,我们最后一棵统计的子树一定是他的重儿子。

    ​ 于是我们得到了清晰地思路:首先找出每个节点的重儿子,然后遍历每个节点,先统计出这个节点所有轻儿子的答案,每次统计完轻儿子都要清空记录次数的(cnt)数组;然后去统计重儿子的答案,不用清空(cnt);然后再去统计一遍所有轻儿子的节点的颜色出现次数,得到当前点的答案。

    CF1437E Make It Increasing

    题目链接

    ​ 无解的情况不用说,挺好想的。

    ​ 如果没有强制不修改的元素,我们考虑怎么做。我们需要满足这个限制条件:(forall i < j, j-i<=a[j]-a[i]),变换一下形式就是:(j - a[j] <= i-a[i])

    ​ 所以我们可以把(i-a[i])看成是序列元素,对这个序列求一个最长不降子序列,那么用总数减去最长不降子序列长度就是要修改的数量。

    ​ 这个题有(k)个点不能改,那我们可以把它看成(k+1)段,每一段都是上面所说的那个问题了。但是要注意一点,求出来的这个最长不降序列一定要包括首尾(开头和结尾那两段分别不用包括首和尾),因为首尾的那两个节点是不能改的。

    2.23 - 2.25

    Luogu P4135 作诗

    题目链接

    ​ 分块好题。

    ​ 首先我们预处理处两个东西:(f[i][j])代表颜色(i)在前(j)个块中出现的次数,(ans[i][j])代表块(i)(j)有多少种颜色。

    ​ 这两个东西都可以(O(nsqrt n))预处理出来。但是预处理(ans)的时候要注意一下方法,就比如一开始我的方法,看上去没问题,但其实是会(TLE)的:

    for(register int i = 1;i <= k; i++) {
     		for(register int j = i;j <= k; j++) {
     			for(register int l = L[j];l <= R[j]; l++) {
    			 	tong[a[l]] ++;
    				if(tong[a[l]] == 1) v.push_back(a[l]);
    			}
    			for(register int l = 0;l < (int) v.size(); l++) {
    				int val = v[l]; 
    				if(!(tong[val] & 1)) ans[i][j] ++;
    			}
    		}
    		for(register int j = L[i];j <= n; j++) tong[a[j]] = 0; v.clear();
    	}
    

    ​ 上面的代码(v)的大小最坏可以达到(O(n))大小的,复杂度就不对了,改成下面这种就好了:

     	for(register int i = 1;i <= k; i++) {
     		int res = 0;
     		for(register int j = i;j <= k; j++) {
     			for(register int l = L[j];l <= R[j]; l++) {
    			 	if(tong[a[l]] & 1) res ++;
    			 	else if(tong[a[l]]) res --;
    			 	tong[a[l]] ++;
    			}
    			ans[i][j] = res;
    		}
    		for(register int j = L[i];j <= n; j++) tong[a[j]] = 0; v.clear();
    	}
    

    ​ 预处理完这两个东西也就好做了,之后就和正常分块一样。统计答案的时候非常巧妙:

    res = ans[x + 1][y - 1];
    for(register int i = 0;i < (int) v.size(); i++) {
        int val = v[i];
        int tmp = t[val][y - 1] - t[val][x];
        if(tmp && !(tmp & 1)) res --;
        tmp += tong[val];
        if(!(tmp & 1)) res ++;
    }
    

    (v)中存的是两边散块的颜色,如果整块中这个颜色的次数是偶数,就(res) --,因为之后如果总和是偶数的话还会加回来,总和是奇数的话那么本来就该减一次。

    Luogu P1903 [国家集训队]数颜色

    题目链接

    ​ 带修莫队板子题。

    ​ 与普通莫队不太一样的地方:对于每个询问操作再记录一个它前面有几次修改。

    ​ 在询问的时候,先把两个指针移动到对应区间,然后还需要一个指针移动到当前询问之前的最后一个修改位置。

    ​ 这个新的指针移动到哪里,它就执行之前所有的修改操作。

    ​ 当然,排序也要改一下:

    int cmp(ques x, ques y) {
    	if(pos[x.l] == pos[y.l]) {
    		if(pos[x.r] == pos[y.r]) return x.t < y.t; // t是当前询问前面有几次修改。
            else if(pos[x.l] & 1) return pos[x.r] > pos[y.r];
            else return pos[x.r] < pos[y.r]; // 奇偶排序
        }
        else reurn pos[x.l] < pos[y.l];
    }
    

    Luogu P3857 [TJOI2008]彩灯

    题目链接

    ​ 线性基。

    ​ 每个开关都可以控制一些彩灯,我们把可以控制的地方看成1,不可以控制的地方看成0,那么每一个开关就是一个01串。如果要按下当前开关,那么相当于对当前灯的开关状态异或上这个01串。

    ​ 我们现在想要知道可以得到多少种灯的开关状态,也就是任意开关组合,可以得到不同的01串的个数,这正好就可以用线性基做。

    ​ 我们把这些01串的线性基构造出来,假如说线性基的大小是(K),那么最终答案就是(2^K)

    Luogu P4570 [BJWC2011]元素

    题目链接

    ​ 线性基。

    ​ 我们如何选择石头才能使总和最大呢?答案是:贪心的选,也就是从魔力值最大的开始选,能选则选。

    ​ 为啥呢?我们考虑当前有(a, b, c)三个魔法石,满足:(a oplus b oplus c =0,val[a] > val[b] > val[c])

    ​ 根据异或的性质:(aoplus b=c,aoplus c = b, b oplus c= a),那么既然三个中只能选两个,那我们为什么不选最大的那两个呢?

    ​ 所以现在我们可以得出清晰地思路:将所有石头按魔力值排序,然后一个一个插入线性基,若能插进去就把这个石头的价值统计到答案里。

    Luogu P4869 albus就是要第一个出场

    题目链接

    ​ 还是线性基。

    ​ 首先我们得知道一个线性基的性质:

    ​ 设原集合大小为(N),线性基大小为(B),显然线性基可以异或出(2^B)个数,那么每个数值的个数就是(2^{N - B}),从题目中的样例解释就可以看出来了。

    ​ 为什么呢?我们可以知道不在线性基中的元素个数是(N-B),对于任意一个不在线性基中的元素,我们可以用线性基的若干元素异或和得到,那么对于任意一个由不在线性基中元素异或得到的数(x),肯定也可以用线性基的若干元素的异或和得到。

    ​ 每一个数(x)都可以从线性基中找到一个一样的异或和,它们两个异或后得到0。一共会有(2^{N-B})(x),所以就有(2^{N-B})个0,其他数字应该也是一样的。

    ​ 知道了这个性质之后就可做了。

    ​ 我们二分一个排名,然后用线性基求(k)小值算出这个排名上的数字是多少,判断一下就好了。最后记得加上0的个数。

    考试题 吉利数

    ​ 题目大意:

    ​ 计算这样的(n)位数个数:每一位只能出现奇数,1和3必须出现并且必须是偶数次。(n <= 1e15)

    ​ 矩阵加速 + 组合数学。

    ​ 设(a_n)代表每一位只出现奇数,1,3出现了偶数次。

    ​ 设(b_n)代表每一位只出现奇数,1出现了偶数次,3出现了奇数次(或者是3出现了偶数次,1出现了奇数次)。

    ​ 设(c_n)代表每一位只出现奇数,1,3出现了奇数次。

    ​ 可以得到:(a_n = 3a_{n-1}+2b_{n - 1})(b_n=3b_{n-1}+a_{n-1}+c_{n-1})(c_n = 3c_{n -1}+2b_{n-1})

    ​ 然后矩阵加速就好了。但是少考虑了一点,1和3必须出现,而dp过程并没有考虑。

    ​ 我们考虑容斥一下:2 *(减去1没有出现,3出现了偶数次的情况),再加上1,3都没有出现的情况。

    ​ 1没有出现,3出现偶数次的情况:(sum_{i=0}^n[2mid i] C_n^{i}*3^{n-i}).

    ​ 我们考虑怎么求这个,有二项式定理:

    ((3+1)^n=sum_{i=0}^nC_n^{i}*3^{n-i})

    ((3-1)^n=sum_{i=0}^n(-1)^iC_n^{i}*3^{n-i})

    ​ 上下两式相加在除以2就可以得到要求的式子,而这个式子的值就是(2^{n-1}+2*4^{n-1})

    Luogu P4035 [JSOI2008]球形空间产生器

    题目链接

    ​ 高斯消元。

    ​ 有(n+1)个点,编号从0到(n),球心设为(O)

    ​ 直接上式子(dist(n1,O)=dist(n0,O))

    ​ 这样可以列出(n)个式子,拆开化简可以把平方项消掉,直接高斯消元就好了。

  • 相关阅读:
    Spark学习笔记2(spark所需环境配置
    Spark学习笔记1(初始spark
    zookeeper基本讲解及基本命令和配置 (转)
    计算机网络面试常考(转载)
    C++面试笔试题汇总
    复杂指针解析
    如何限制一个类对象只在栈(堆)上分配空间?
    虚函数实现机制
    C++内存分配方式详解
    C++中指针和引用的区别(转载)
  • 原文地址:https://www.cnblogs.com/czhui666/p/14434132.html
Copyright © 2020-2023  润新知