LINK:qiqi20021026的T1
考场上只拿到了50分的(nq)暴力。
考虑一个区间和一个区间配对怎么做 二分图最大带权匹配复杂度太高。
先考虑LCS的问题 常见解决方法是后缀数组/trie树.
一个贪心是 每次让贡献最大的一对配对是最优的策略。
具体证明可以利用扰动法 或者观察法。
即设(p,q,l,r) 分类讨论一下情况就行辣 怎么证明全局最优?可以发现任意两个匹配都是这样最优 交换会变得更差 这样应该可以说明全局最优了吧?
这个贪心直接做复杂度还是很高 可以考虑trie树上做 那么就是在LCA处匹配 匹配不了到父亲处.
这样复杂度就是(ncdot q)的了.
考虑正解:写完暴力我一度认为这题感觉是一个二维莫队。
但是我发现每次多加一个字符串的贡献非常难处理 所以就不会了。
一个非常套路 且我见过很多次就是没反应过来的套路 是 观察这个贡献 后缀长度 其实可以均摊到路径上的节点上.
即对于每个节点取 min(a,b)即可得到贡献。
这样一个串就对应到了一条路径上 且每个节点的min(a,b)这种贡献都是可以O(1)计算的。
然后考虑莫队 直接 序列上(trie树此时其实也是一个序列)莫队+序列上莫队 这样一共存在四个指针。
分块的大小要调整好 可以奇偶优化分块 复杂度不太清楚 (反正轻轻松松跑过5e5
code
//#include<bitsstdc++.h>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 1000000000
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define gt(x) scanf("%d",&x)
#define gi(x) scanf("%lf",&x)
#define put(x) printf("%d
",x)
#define putl(x) printf("%lld
",x)
#define gc(a) scanf("%s",a+1)
#define rep(p,n,i) for(RE int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define fep(n,p,i) for(RE int i=n;i>=p;--i)
#define vep(p,n,i) for(RE int i=p;i<n;++i)
#define pii pair<int,int>
#define mk make_pair
#define RE register
#define P 1000000007
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define EPS 1e-4
#define sq sqrt
#define S second
#define F first
#define mod 1000000007
#define len(x) t[x].len
using namespace std;
//无奈 太菜了 签到题都只能暴力 我真是个弱智./kk
const int MAXN=10010,maxn=500010;
int n,Q,len,cnt,ans,id=1,B=100;
int t[MAXN][26],pos[MAXN],sum[maxn],wl[MAXN],wr[MAXN],c[MAXN];
struct wy{int l,r,L,R,id;}q[maxn];
string a;
inline void insert()
{
int now=1;
vep(0,a.size(),i)
{
int ww=a[i]-'a';
if(!t[now][ww])t[now][ww]=++id;
now=t[now][ww];pos[++cnt]=now;
}
}
inline int cmp(wy a,wy b)
{
if(a.l/B!=b.l/B)return a.l<b.l;
if(a.r/B!=b.r/B)return a.l/B&1?a.r<b.r:a.r>b.r;
if(a.L/B!=b.L/B)return a.r/B&1?a.L<b.L:a.L>b.L;
return a.L/B&1?a.R<b.R:a.R>b.R;
}
inline void add(int x)
{
++c[pos[x]]>0?++ans:--ans;
}
inline void del(int x)
{
--c[pos[x]]<0?++ans:--ans;
}
int main()
{
//freopen("1.in","r",stdin);
freopen("qiqi.in","r",stdin);
freopen("qiqi.out","w",stdout);
ios::sync_with_stdio(false);
cin>>n>>Q;
rep(1,n,i)
{
a="";cin>>a;
reverse(a.begin(),a.end());
wl[i]=cnt+1;insert();wr[i]=cnt;
}
rep(1,Q,i)
{
int l,r;cin>>l>>r;
q[i].l=wl[l];q[i].r=wr[r];
sum[i]+=wr[r]-wl[l]+1;
cin>>l>>r;q[i].id=i;
q[i].L=wl[l];q[i].R=wr[r];
sum[i]+=wr[r]-wl[l]+1;
}
sort(q+1,q+1+Q,cmp);
int l=1,r=0,L=1,R=0;
rep(1,Q,i)
{
while(l>q[i].l)add(--l);
while(l<q[i].l)del(l),++l;
while(L>q[i].L)del(--L);
while(L<q[i].L)add(L),++L;
while(r<q[i].r)add(++r);
while(r>q[i].r)del(r),--r;
while(R<q[i].R)del(++R);
while(R>q[i].R)add(R),--R;
sum[q[i].id]-=ans;
}
rep(1,Q,i)put(sum[i]>>1);
return 0;
}