广二&雅礼考试-解题报告
广二
6.11
T1原题。
脑部进食
高斯消元,发现有一维单调,所以可以按层高斯消元。
游戏
没有题解???
ri,曾经脑子里闪现过二分的思路,但是后来又否决掉了:博弈题不能二分吧。瞄了眼题解,竟然真是二分!
博弈论二分!
那就好说了,我们把
但是我们需要利用这道题的性质才能快速求出匹配。(恩不错这里我大概是想出来了
把两个序列排序后,把nm个点放在网格里,那么点就分成了两种,一种是左上方的,一种是右下方的。我们求最大匹配不方便,可以转为最大独立集,我们的最优策略一定是选一个点
6.12
集合
大力讨论
棋盘
啥破玩意。
打表可以发现必胜条件是:
通常情况下就是<SDOI移动金币>的阶梯Nim的胜负态。
当k是偶数且特殊位置不在左边第二个的时候,第一段的间隔-1.
然后考虑如何统计方案数。这里的方法把SDOI2019日穿了
先回忆SDOI那道题的做法:我们按位DP,然后要存下之前用的数字和。
那么这道题我们显然不能这么做。但是,在状态里记录数字和其实用处不大,我们考虑n的每一位,肯定是这一位填的数,加上后边进位的数得到的,这和梦幻岛宝珠这道题很像!我们可以
序列
注意到大小关系的变化量是
正解在这个基础上优化一下就行了。我们建出后缀树(不要老是想着把SAM的parent树的节点看成本质不同的子串,它们其实就是(反串的)后缀树上的节点),注意到一次大小关系的改变其实就是把Trie树上每个点最小的儿子挪到子树最后的过程,这个变化总共是
6.17
Distinct Boxes
唔...这题有点强的。
Snuke 有 R 个红色球和 B 个蓝色球。他想把它们分到 K 个盒子中,使得不存在空盒子并且任意两个盒子都可通过球的颜色、数量区分。求 K 的最大值。
三重二分!
首先二分答案k
然后,问题变成从平面上选取k个点,设它们的横纵坐标和分别为
然后,如何求凸壳上的一个点?我们二分斜率,把每个点的权值变为
复杂度很玄学的是
Multiple of Nine
lx讲课的bell状压题。
Paths
可以转化为求有多少三元环和四元环经过这个点。
6.19
无向图
分治+并查集。
线段树
线段树定位节点+树上莫队+点分治。
玩游戏
像这种**题,写70分bitset跑路就可以了。
6.20
两道原题...
T2
最近不知道在干嘛,思考的时候老是走神,不专注,经常在那愣神浪费时间。
给长度为n的序列填上
题目的暗示很明显,这样连续的不合法段最多长
f[0][0]=1;
for(int i=1;i<=n;++i)
for(int j=1,x=0;j<=i && c[j];++j,x=!x)
{
f[i][0]=(f[i][0]+f[i-j][ x]*1ll*c[j])%MOD;
f[i][1]=(f[i][1]+f[i-j][!x]*1ll*c[j])%MOD;
}
printf("%d\n",sub(f[n][0],f[n][1]));
6.23
A是APIO数据备份...
B
这是道水题,但是我咋想了那么久...
首先显然的贪心是正确的,就是从后向前扫一遍,遇到前缀和<0的就删掉,然后求这个序列的前缀最小值(相当于再从前往后扫一遍,删掉不合法的),那么正解肯定是扫描线了,但是如何维护这个信息呢?
前缀和最小值好维护。我们希望找出所有后缀和小于0的位置,如何维护呢,其实很简单,维护一个栈,遇到-1就加入,这个位置设为0;遇到1把这个位置设为1,把栈顶位置的值设成-1,弹出栈顶。查询时要把答案加上栈里的元素
你可能会想,当后边+1了,后缀和为-1的位置都合法了,这不是很多位置吗,这是假的,如果两个位置的后缀和都是-1,那么之间的括号是匹配的,那么这一段我们是不用动的!
C
哇塞,这道题牛啊。
显然我们要在AC自动机上跑。但是如何区间查询呢(我只会辣鸡的根号(可能还带log)的做法),正解十分暴力:
我们把母串S和模板串都放到AC自动机上,对于每个节点,我们维护它到根的串的每个后缀的答案,这个答案必须包含后缀的第一个位置,换句话说,每个点有个可持久化treap,里面有len个节点,表示每个后缀的前缀的答案和。这样做有什么好处呢?首先询问很方便。并且,建立这棵树的方法也很巧妙:注意到这个串长度
6.24
A
显然这道题有决策单调性,但是直接做是
我们把所有商店按照
自己YY的做法:
首先这个DP是凸的,那么我们可以wqs二分。于是对每个商店我们贪心的选
B
可做的计算几何
C
没有题解...
雅礼
6.20
A
退役了退役了这都没想出来...
对于每个节点 u,
见到树上路径,可以点分治。处理过中心的路径,发现贡献是把每个深度的点数cnt和w卷积起来。
6.22
A
比较简单的数据结构,注意细节的讨论。eg. 不能有重复的数字。
B Epidemic
哇塞好题。看另一篇博客吧。
C
一个排列最多2次就可以交换完成。当一个排列的所有环的大小
然后...DP+Bell数爆搜...不会了。
6.23
A是原题...
B
二叉树!
感觉这些树上交互题都是随机剖分/点分治/LCT+链上二分。
既然要链上二分,我们先随便问出一条到根的链。然后把这条重链上的点排序(注意到祖先后代关系具有传递性),然后其他点可以通过链上二分找到位置。复杂度是随机剖分。
但是有一个问题:怎样确定在哪个轻儿子里?
似乎做法是这样的,我们写一个函数
如果对比较次数有限制,不要用sort要用stable_sort!
C
这道题的难点在于恶心...
我们只要解决链上的问题就行。显然相交一定是位置相邻的点,而相交之前位置的相对关系是不会变的,所以我们用set维护即可。
6.25
这场我和ywy也考了...然后发现被雅礼神仙虐的很惨...
A
考场上写的是删掉一条链,然后把链上的点的其他的儿子接到根,同时根维护一个优先队列存每个儿子。
其实上边的做法就等价于长链剖分,把每条长链的长度放到vector里取前K大(nth_element)...
const int N=4e6+5;
int n,K;
int val[N],fa[N];
int head[N],cnte=1,v[N],nx[N];
il void adde(const int uu,const int vv) {v[++cnte]=vv,nx[cnte]=head[uu],head[uu]=cnte;}
LL dep[N],len[N]; int cnt;
void solve()
{
for(ri x=n; x>=1; --x)
{
if(!head[x]) {dep[x]=val[x]; continue;}
for(ri i=head[x]; i; i=nx[i])
if(dep[x]<dep[v[i]]+val[x])
{
if(dep[x]) len[++cnt]=dep[x]-val[x];
dep[x]=dep[v[i]]+val[x];
}
else len[++cnt]=dep[v[i]];
}
len[++cnt]=dep[1];
nth_element(len+1,len+K,len+cnt+1,greater<LL>());
LL ans=0;
for(ri i=1; i<=K; ++i) ans+=len[i];
out(ans);
}
signed main()
{
#ifdef M207
freopen("in.in","r",stdin);
// freopen("ot.out","w",stdout);
#endif
in(n,K,val[1]);
for(ri i=2; i<=n; ++i)
{
val[i]=(val[i-1]*23333333ll+6666666)%1000000007;
fa[i]=(val[i]^23333333)%(i-1)+1;
adde(fa[i],i);
}
solve();
return 0;
}
B
线段树神仙题?
C是原题。
6.26
A是原题
B
min_25筛加强版?看另一篇博客:Powerful number吧
C
首先如果没有边的限制,我们是可以枚举第一个“自由位”的位置,
有了边的限制,我们就要容斥,强制相等,这形成了若干联通块,每块只有限制最小的点有用(然后可以Bell数爆搜)。但是怎样优化到指数级复杂度?
看了题解恍然大悟:既然方案数只和每个联通块的最小值有关,我们可以直接暴力状压DP
分析性质,找到到底需要啥。
求每个联通块的容斥系数有两种方法:类似<城市建设>一样减法,或者枚举删掉最小的点后,次小的点构成的联通块集合。
然后分析复杂度,显然瓶颈在后面的背包,我们以填表法分析复杂度
我们每次枚举的是最小的在t中的点i,考虑点i贡献多少复杂度。
i前边的要么不在s,要么在,这是
i后边的,我们枚举比i大的点的在
s中的子集,所有点要么不在s,要么在s且没在枚举的子集里,要么在枚举的子集里。
所以总复杂度
6.28
game
有 n 个人,要分成若干组,对于第 i 个人,它所在的组的人数不能超过 ai ,问分组的方案数模 998244353 的值。
组与组之间是不可区分的,每组的人也是无序的,但人与人之间是可以区分的。
先把a排序
先写出
貌似题解的做法挺巧妙的,也不依赖模数:
rook
一月份安师大考试的原题...但是好恶心啊想鸽了。
collect
(标程13k,摸了?)
但是这个思路是很值得学习的。
首先我们可以求出走z步后横坐标为i的生成函数:
我们如何计算贡献呢?我们考虑求出走
这个咋求呢,我们需要把坐标“循环移动”
其实很简单:
这个
如果没有K的限制,我们要求的就是
这其实就是多项式等比数列求和,倍增即可。
那么有K的限制呢?由于左边
的值,然后在F数组的对应位置减去即可!
6.29
T1
26个点的费用流...**zzt的数据是假的调了我半天...
T2
构造题要勇于猜下界
老虎最近得到了一个排列 a。这一次,老虎决定将这个序列排序。
老虎的排序方法是这样的:每次他会选择排列 a 的一个子序列 b,删去这个子序列后,将这个子序列插回到原序列的最前面。
老虎想要考考蒜头,因此他想要蒜头回答出,至少需要几次操作可以将序列 a 排好序。同时,为了说明你不是随便报的,你需要输出操作的方案
按位构造好题。考点:基数排序
看到这道题有一个显然的贪心是把序列分段,分为极长的值域连续且位置递增的段,我们段内部的顺序肯定不用动。然后段之间需要重新排序,那么我们按位从低到高考虑,每次把这一位是0的丢到前边,log次后就排好序了。
T3
神仙构造题,摸了。
7.1
最后一天啦
T1
这一天,老虎和蒜头又聚集到一起讨论有趣的问题了。与往常类似地,蒜头又不知道从哪里摸出了一个无向平面图。讨论完毕后,蒜头提议进行一个游戏:轮流给该图加上一条边,直到无论增加上哪一条边图都会变得不是平面图,无法操作者败。
游戏结束。毫无疑问,老虎又输了,然而老虎现在关心的并不是这样的问题。老虎最近刚刚学习过三分图理论,因此他想要知道,有多少种加边的方案 (不能加重边和自环),使得加边后的图是一个三分图。
一个图被称为三分图,当且仅当存在一个方案,可以将图划分成三个点集,使得不存在任意一条边在一个点集内部。
首先原图必须是三分图。确定相邻两个点的颜色后,就可以进而确定所有其他点的颜色或判断无解(似乎是因为平面图的性质),我们只要在同颜色点之间加边即可。
T2
我们考虑答案可能的最大值,那就是离散化后,对于相邻关键点之间的区间,我们希望跨过它的区间,一半是黑色一半是白色。
事实上这个上界是可以达到的。我们维护和当前段有交的区间,假如加入一个区间后数量为奇数,那么我们不用处理(因为它黑白无所谓);如果为偶数:
- 如果前一个操作是加入,那么两个区间的颜色不同
- 否则,两个区间的颜色相同
删除也差不多。代码实现起来有一些Trick。
T3是通信题...
3.22
T1
似乎正解就是按题意模拟...?没想到这么简单啊。
我们先预处理出lim数组,表示被修改的值的第k大至少为lim[k],再记录要合法至少要改变几个点的值。
int L=0,R=0;
REP(i,1,n) if(d[Min[id[i]]]<a[i]){L=i;break;}
if(L==0) return printf("0\n"),0;
DREP(i,n,1) if(d[Min[id[i]]]<a[i]){R=i;break;}
int Max=0;
for(int i=L,j=1;i<=R;++i){
while(j<=n && a[i]>d[Min[id[j]]]) ++j;
if(j>i) lim[j-i]=a[i],chkmax(Max,j-i);
}
int ans=inf;
for(int i=1,j;i<=L;i=j+1){
j=i;
while(j<n && Min[id[j+1]]==Min[id[i]]) ++j;
if(j-i+1<Max) continue;
REP(k,i,j) stk[++top]=Sec[id[k]];
sort(stk+1,stk+top+1,greater<int>());
int flag=1;
REP(k,1,Max) if(stk[k]<lim[k]){flag=0;break;}
if(flag) chkmin(ans,a[R]-d[Min[id[i]]]);
top=0;
}
我们枚举修改某个点,把到根最小值位于这个点的点集找出来,排序,和上面的lim数组对照,看是否按位大于。
T3
这是好交互题:询问3个下标,返回min+max,在