要换换名字
考虑如果一个串有大于 (n) 个子序列,那么一定不用考虑,所以找到每个串的前 (n+1) 个子序列
显然在串比较长的情况下子序列长度不会太大,用 (map) 存标号就行了
找子序列的部分使用子序列自动机,注意空间第一维要开长度大小
剩下的显然是二分图匹配,写一个网络流,一开始源点向子序列的边不带流量
最后枚举答案 (ans),对于长度为 (ans) 的子串维护即可
动态半平面交
真正的厉害题,但是真的不清楚为啥 (skyh) 说这个有手就行
前置
- 区间 (lcm)
对于每个质因子的次幂数维护一个单调栈,套上一个扫描线或者可持久化就行了
- ( exttt{dsu on tree})
先树剖一下,然后对于每个枚举的点,先计算轻子树答案,清空贡献
再计算重子树答案,不清空贡献,计算当前节点答案,最后扫一次轻子树贡献
回到本题,可持久化一个线段树,树上的深度存对应点答案的贡献
(深度对应贡献的做法是我没想到的)
对树进行 ( exttt{dsu on tree}) 来预处理贡献
写的时候是很艰难的,但是最后还是弄明白写出来了
inline void calc2(int x, int anc) {
for (auto p : div[x]) {
int num = id[p.first];
auto it = s[num].lower_bound(make_pair(dep[x], 0)), tp = it;
if (it != s[num].begin()) {
tp = it; //在深度小于之的有比它次数更大的
--tp;
if (tp->second >= p.second)
continue;
} else if (it != s[num].end() && it->first == dep[x] && it->second >= p.second)
continue;//深度相同有次数更大的
int lst;
if (it == s[num].begin())
lst = 0;
else {
tp = it; //维护差分
--tp;
lst = tp->second;
}
while (1) { //单调栈弹栈
it = s[num].lower_bound(make_pair(dep[x], 0));
if (it == s[num].end())
break;
upd(rt[anc], 1, mx, it->first,
ksm(p.first,(it->second - lst) * (mod - 2) % (mod - 1))); //把单调栈后面的部分的都先去掉
if (it->second > p.second)
break;
lst = it->second;
s[num].erase(it);
}
it = s[num].lower_bound(make_pair(dep[x], 0));
if (it == s[num].begin())
lst = 0;
else {
tp = it; //差分:当前差前面
--tp;
lst = tp->second;
}
upd(rt[anc], 1, mx, dep[x], ksm(p.first, p.second - lst)); //更新答案
if (it != s[num].end())
upd(rt[anc], 1, mx, it->first, ksm(p.first, it->second - p.second)); //后面差当前
s[num].insert(make_pair(dep[x], p.second)); //加入当前维护的集合
}
for (reg int i = head[x]; i; i = e[i].nxt)
if (e[i].to != fa[x])
calc2(e[i].to, anc); //不需要判重儿子
}//dsu on tree 把轻子树的贡献统计
获取名额
题面转化一下就是 (ans=1-prod 1-frac{a_i}x)
直接搞前缀积显然精度是不够的,那么考虑如何分治求区间询问
这个显然是个付公主的背包,也就是
[ans=1-e^{sum ln(1-frac{a_i}x)}
]
(exp) 函数是能用的,用 (st) 表进行最值分治,如果当前区间最值 (ge x imes 0.4) 分治两边
如果不是那么维护展开后的前 (30) 项
卡精度的一个地方是要先除掉 (max) 否则展开幂大了之后就爆精度了