题意:
给母串S
记子串 S l , r S_{l,r} Sl,r在母串中出现次数为 C l , r C_{l,r} Cl,r
对于每一个次数都有一个权重 w i w_i wi
q组询问,对于一个子串 T = S [ l , r ] T=S[l,r] T=S[l,r],A要选出k个A的子串 R i = S l i , r i R_i=S_{l_i,r_i} Ri=Sli,ri,使得 T ∈ s u f f i x ( R i ) T\in suffix(R_i) T∈suffix(Ri),k大于0之外没有限制,由此,每一个子串 R i = S l i , r i R_i=S_{l_i,r_i} Ri=Sli,ri,A都会获得一堆个数为 W C l i , r i W_{C_{l_i,r_i}} WCli,ri的石堆,B在A的基础上要去掉若干个石堆,但不能全去掉,使得A先手做Nim博弈不会赢。
当然A不会让B得逞,在确保自己能赢得情况下,A要使 ∑ W C l i , r i \sum W_{C_{l_i,r_i}} ∑WCli,ri最大,对于每个询问输出这个最大值
W < 2 58 , ∣ S ∣ ≤ 1 0 5 , q ≤ 2 ∗ 1 0 5 W<2^{58},\ |S|\leq 10^5,\ q\leq 2*10^5 W<258, ∣S∣≤105, q≤2∗105
不想让B得逞,那么选出的石堆权值要线性无关,并使权和最大
这很容易证明贪心地从大到小往线性基里丢,取线性基里面的元素作为答案,是对的。
要求T为选出串的后缀,不难想到建SAM
存在T这个后缀,等价于在parent树上T的祖先,也就是说你要倍增找到T的位置,将询问挂在那个点上,最后遍历parent树启发式合并线性基,线性基每次加点如果能加进去就加进去,如果加不进去考虑能不能将表示它的权值最小那个替换掉,和动态生成树有异曲同工之妙,也就是线性基的时候顺便记一下每个位置由哪几个构成
总复杂度 O ( n log n log W ) O(n\log n\log W) O(nlognlogW)
#include
#define N 200100
#define fo(i,a,b) for(int i=a,__=b;i
#define fd(i,a,b) for(int i=a,__=b;i-->__;)
#define ul unsigned long long
#define ll long long
using namespace std;
struct base{
ll a[58],b[58],c[58];int tot;
void init(){
memset(a,0,sizeof a);
memset(b,0,sizeof b);
memset(c,0,sizeof c);
tot=0;
}
void ins(ll v){
ll V=v,e=0;
fd(i,58,0)if(V&1ll<<i){
if(!a[i]){
a[i]=V;c[tot]=v;
b[i]=e|1ll<<tot++;
return;
}V^=a[i];e^=b[i];
}ll mn=1e18;int k;
fo(i,0,tot)if(e&1ll<<i)
if(c[i]<mn)mn=c[i],k=i;
if(mn>=v)return;
fo(i,0,58)if(b[i]&1ll<<k){
fd(j,58,i)if(b[j]&1ll<<k)a[j]^=a[i],b[j]^=b[i];
break;
}e=1ll<<k;c[k]=v;
fd(i,58,0)if(v&1ll<<i){
if(!a[i]){
a[i]=v;b[i]=e;return;
}v^=a[i];e^=b[i];
}
}
ul sum(){
ul r=0;
fo(i,0,tot)r+=c[i];return r;
}
}b[N];
int nx[N][26],pre[N],dp[N],cnt,ls,t[N],sz[N],hvy[N],ps[N],n,q,f[18][N];
ll w[N];
ul ans[N];
vector<int>r[N],qry[N];
void init(){
memset(nx,0,sizeof nx);
memset(dp,0,sizeof dp);
memset(pre,0,sizeof pre);
memset(t,0,sizeof t);cnt=ls=1;
}
int nw(int d){dp[++cnt]=d+1;return cnt;}
void ins(int c){
int now=nw(dp[ls]);
for(;ls&&!nx[ls][c];ls=pre[ls])nx[ls][c]=now;
if(!ls)pre[now]=1;else{
int u=ls,v=nx[u][c];
if(dp[v]==dp[u]+1)pre[now]=v;else{
int _v=nw(dp[u]);
memcpy(nx[_v],nx[v],sizeof nx[v]);
pre[_v]=pre[v];pre[now]=pre[v]=_v;
for(;u&&nx[u][c]==v;u=pre[u])nx[u][c]=_v;
}
}ls=now;
}
void dfs(int x){
sz[x]=1;
int y,mx=0,p=0;
fo(i,0,r[x].size()){
y=r[x][i];dfs(y);
t[x]+=t[y];sz[x]+=sz[y];
if(sz[y]>mx)mx=sz[y],p=y;
}hvy[x]=p;
}
void put(int x,int y){
b[y].ins(w[t[x]]);
fo(i,0,r[x].size())put(r[x][i],y);
}
void work(int x){
fo(i,0,r[x].size())work(r[x][i]);
b[x]=b[hvy[x]];
b[x].ins(w[t[x]]);
fo(i,0,r[x].size())if(r[x][i]^hvy[x])put(r[x][i],x);
if(b[x].tot){
ul r=b[x].sum();
fo(i,0,qry[x].size())
ans[qry[x][i]]=r;
}
}
int main(){
int _;scanf("%d",&_);b[0].init();
while(_--){
scanf("%d\n",&n);init();
for(int i=1;i<=n;++i)ins(getchar()-97),ps[i]=ls;
for(int i=1;i<=cnt;++i)r[i].clear(),qry[i].clear();
for(int i=2;i<=cnt;++i)r[pre[i]].push_back(i),f[0][i]=pre[i],b[i].init();
fo(j,1,18)for(int i=1;i<=cnt;++i)f[j][i]=f[j-1][f[j-1][i]];
for(int i=1;i<=n;++i)t[ps[i]]=1;
for(int i=1;i<=n;++i)scanf("%lld",w+i);
scanf("%d",&q);
for(int i=1;i<=q;++i){
int l,r;scanf("%d %d",&l,&r);
int x=ps[r];
fd(j,18,0)if(dp[f[j][x]]>=r-l+1)x=f[j][x];
qry[x].push_back(i);
}dfs(1);work(1);
for(int i=1;i<=q;++i)printf("%llu\n",ans[i]);
}
}