2023.2.7 省选模拟赛总结
1.时间安排
7:30~8:30
T1是关于阶梯覆盖的构造,T2大概是字符串重工业,T3数位DP+数学。
先想T1的构造,\(n\) 次操作是浅显的,考虑怎么 \(O(\frac{n}{3})\) 做,一开始想的是把三行合并起来,看小的多余的部分怎么一起消除掉,但是没找到合适的方法。
之后从分治的方向入手,发现可以每次把两个阶梯用2次切掉两个大块,再用最多2次切掉一行一列,转化成两个规模除以2的子问题,但是一求和发现操作次数甚至比 \(n\) 多,实际写了一下(顺带写了spj方便之后调试),确实如此,只好先放弃。
8:30~9:30
T2的所有路径可以很方便 \(O(n^2)\) 求出,但是需要对每条路径形成的字符串求他的本质不同子串数量,可以用 sam 求但是不想写,想了想写了离线后扫描线非常简单好调,但是问题在于如何找到路径对应的子串位置,只需要打个标记,右端点是标记,左端点减去路径长度即可。
(明明很简单的东西却想了挺久,字符串还是不太熟练。)
没找到题面中的黑点有什么特殊性质,不过即使找到性质之后也逃不了求区间本质不同子串数量,已知的做法是离线后lct+bit,可以宣布放弃。
9:30~10:30
先把T3暴力写了,然后想 \(k=1\),没有数相同可以用二项式反演变为求至少 \(k\) 个数相同的方案数,然而写完后发现最重要的消序问题没考虑到,原问题求的是无序序列,但是这里无序序列很难求,写完的代码只能求有序的,想了很久没想到怎么用有序的推出无序的。
只会想暴力DP,联想到之前做过类似的题(或者说这题的部分分),需要斯特林反演,当时也没订出来,果断放弃。
10:30~12:00
推T1构造,始终在尝试从分治和分块角度拆阶梯,但是都寄了。
result:
T1:15 T2:30 T3:5
2.总结
T1:
%Larunatrecy 场切
不知道是怎么联想到二进制分组的,Larunatrecy说他认为相等的下标元素为0就相当于不等的必须被至少覆盖一次,由此想出只有对角线是0的二进制分组做法,进而推广到正解,很高妙。
覆盖构造问题做的比较少,第一次见到与二进制分组结合的覆盖构造。
考虑只有对角线时,枚举所有二进制位 \(b\),把行这位是0和列这位是1的放在一组,再把行列的要求反过来做一遍,容易发现相等下标的位置不会被覆盖,任意不同下标的位置都会被至少覆盖一次(可以考虑反证法)。
有两条对角线可以把矩阵的列拆成奇偶两部分,奇数行拼出的矩阵可以发现相邻两行完全相等,可以并做一行,偶数行类似,但是第一行是全1行,特殊判断一下,套用上文中的对角线做法可以做到 \(4logn\),大概50次左右。
不妨取 \(logn=13\),可以取所有13位且有6个1的二进制数来表示行列号,这样只需要做一遍即可(证明也可以用反证法,容易证明一对不等的不被分入某一组是矛盾的),选6位的原因是因为 \(C(13,6)\) 是13这一行组合数中最大的(当然 \(C(13,7)=C(13,6)\)),越多越好嘛,\(C(13,6)>1500\),足够覆盖所有的行列。
总操作次数是 \(2logn=26\),足够通过。
T2:
1e5确实是留给上述重工业的部分分,还没写出来。
upd on 2.7 18:05
在 Larunatrecy 的提醒下,改正了代码后缀树部分的细节错误,已经过掉 \(n\leq 1e5\) 的部分分。
在这里放一下求出所有后缀树边代表的字符串在原串对应区间的代码,备忘
struct SAM{
int pos[N], fan[N];
struct node{
int ch[2], fa, len;
}sam[N<<1];
int cnt, lst;
SAM(){cnt=lst=1;}
void extend(int id, int x){
int p=lst, np=lst=++cnt;
pos[id]=cnt; fan[cnt]=id;
sam[np].len=sam[p].len+1;
for(; p&&!sam[p].ch[x]; p=sam[p].fa) sam[p].ch[x]=np;
if(!p) sam[np].fa=1;
else{
int q=sam[p].ch[x];
if(sam[q].len==sam[p].len+1) sam[np].fa=q;
else{
int nq=++cnt; fan[nq]=fan[q];
sam[nq]=sam[q]; sam[nq].len=sam[p].len+1;
sam[np].fa=sam[q].fa=nq;
for(; p&&sam[p].ch[x]==q; p=sam[p].fa) sam[p].ch[x]=nq;
}
}
}
}S;
int main(){
for(int i=1; i<=n; i++) S.extend(i, s[n-i+1]-'0');
for(int i=2; i<=S.cnt; ++i){
int l, r;
r=S.fan[i]; l=r-S.sam[i].len+1; r=r-S.sam[S.sam[i].fa].len;
l=n-l+1; r=n-r+1; swap(l, r);
// cout<<l<<' '<<r<<endl;
}
return 0;
}
正解是sam上的性质,还没理解。
T3:
非常复杂的生成函数,正解放弃,也没用讲部分分做法,整题暂时放弃。

浙公网安备 33010602011771号