省选做题总结
省选做题总结
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] + 1\mid 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 - sin\alpha*y,sin\alpha*x+cos\alpha*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(n\sqrt 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]\)。
根据异或的性质:\(a\oplus b=c,a\oplus 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[2\mid 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\)个式子,拆开化简可以把平方项消掉,直接高斯消元就好了。