对所有子串建出AC自动机,并求出fail树。考虑对询问区间差分一下后从左到右扫描线处理,那么可以变为两种操作:对fail树上某个子树中所有点的权值 + 1 +1 +1,查询某个字符串对应的所有节点的权值和。
这里给出一个做法,我们可以将每个节点映射到二维平面上,两维坐标分别是fail树上的dfs序和所属字符串的编号,那么原问题就变为给横坐标在某个区间内的所有点 + 1 +1 +1和求出纵坐标 = = =某个数的所有点的权和。
这两个操作都容易用KD-Tree维护,时间复杂度 O ( ( ∣ S ∣ + q ) ∣ S ∣ \mathcal O((|S|+q)\sqrt{|S|} O((∣S∣+q)∣S∣。这种做法常数比较大,跑得比按字符串长度分块慢很多。。。
#include <bits/stdc++.h>
#define end end2
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
struct Point {
int x,y;
Point() {
}
Point(int a,int b):x(a),y(b) {
}
};
bool cmp1(Point x,Point y) {
return x.x<y.x;
}
bool cmp2(Point x,Point y) {
return x.y<y.y;
}
Point p[100005];
namespace KDT {
int ch[100005][2],size[100005],tot;
int minx[100005],maxx[100005];
int miny[100005],maxy[100005];
int addv[100005],num[100005];
ll sumv[100005];
inline void pushdown(int o) {
if (addv[o]) {
int ls=ch[o][0],rs=ch[o][1];
num[o]+=addv[o];
if (ls) {
addv[ls]+=addv[o];
sumv[ls]+=(ll)addv[o]*size[ls];
}
if (rs) {
addv[rs]+