D - XOR Pair
给出 n , m , a , b n,m,a,b n,m,a,b,你需要求出有多少对 x , y x,y x,y,满足 x ∈ [ 0 , a ] , y ∈ [ 0 , b ] , ∣ x − y ∣ ≤ m , x ⊕ y = n x\in [0,a], y\in [0,b], |x-y| \le m,x\oplus y = n x∈[0,a],y∈[0,b],∣x−y∣≤m,x⊕y=n,其中 ⊕ \oplus ⊕表示按位异或。
数据组数 T ≤ 1 0 5 T\le 10^5 T≤105, a , b , n , m ≤ 1 0 18 a,b,n,m\le 10^{18} a,b,n,m≤1018
Sol
逐位考虑 x − y x-y x−y,发现:如果 n n n在 2 i 2^i 2i这一位是 1 1 1,则 x , y x,y x,y在 2 i 2^i 2i这一位上的差要么是 1 1 1,要么是 − 1 -1 −1;如果 n n n在 2 i 2^i 2i这一位是 0 0 0,则 x , y x,y x,y在 2 i 2^i 2i这一位上的差一定是 0 0 0。
先对所有 n n n为 1 1 1的位让 x − y x-y x−y取到 − 1 -1 −1,此时得到的 x − y x-y x−y就是 − n -n −n。则如果 x − y x-y x−y要在 2 i 2^i 2i取 1 1 1,那么就会对原先的数产生额外的 2 i + 1 2^{i+1} 2i+1的贡献( 2 i − ( − 2 i ) = 2 i + 1 2^i - (-2^i) = 2^{i+1} 2i−(−2i)=2i+1)。
设 d d d为一个数,如果 n n n在第 i i i位上为 1 1 1,那么 d d d在第 i + 1 i+1 i+1位上可以为 1 1 1也可以为 0 0 0;否则, d d d在第 i + 1 i+1 i+1位上只能为 0 0 0。我们相当于要求,有多少个 d d d满足 ∣ d − n ∣ ≤ m |d-n|\le m ∣d−n∣≤m并且对应的 x , y x,y x,y满足 x ∈ [ 0 , a ] , y ∈ [ 0 , b ] x\in [0,a],y\in [0,b] x∈[0,a],y∈[0,b]。这是一个经典的数位 d p dp dp问题。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
ll f[70][2][2][2];
int A[70],B[70],C[70],N[70];
ll dfs(int u,int fx,int fy,int fu) {
if(u==-1) return fx&&fy&&(fu||C[0]==1);
if(~f[u][fx][fy][fu]) return f[u][fx][fy][fu];
ll res=0;
for(int x=0;x<2;++x) {
int y=N[u]^x;
if((!fx&&x>A[u])||(!fy&&y>B[u])) continue;
if((!fu&&(x>y)>C[u+1])) continue;
res+=dfs(u-1,fx|(x<A[u]),fy|(y<B[u]),fu|((x>y)<C[u+1]));
}
return f[u][fx][fy][fu]=res;
}
void work(ll a,int A[]) {
for(int i=0;i<70;++i) A[i]=0;
int p=0;
while(a) A[p++]=a&1,a>>=1;
}
int main() {
int T; rd(T);
while(T--) {
ll a,b,m,n;
rd(a),rd(b),rd(n),rd(m);
work(a+1,A);
work(b+1,B);
work(n,N);
work(n+m+1,C);
memset(f,-1,sizeof(f));
ll ans=dfs(64,0,0,0);
work(max(0ll,n-m),C);
memset(f,-1,sizeof(f));
ans-=dfs(64,0,0,0);
printf("%lld\n",ans);
}
return 0;
}
E - Ternary String Counting
有一个长度为 n n n的、字符集大小为 3 3 3的字符串。有 m m m个限制,第 i i i个限制形如 ( l i , r i , x i ) (l_i,r_i,x_i) (li,ri,xi),表示 s [ l i ⋯ r i ] s[l_i\cdots r_i] s[li⋯ri]有 x i x_i xi个不同的字符。问满足所有限制的长度为 n n n的字符串有多少个。答案对 1 0 9 + 7 10^9+7 109+7取模。
n ≤ 5000 , m ≤ 1 0 6 n\le 5000,m\le 10^6 n≤5000,m≤106
Sol
朴素的 d p dp dp:设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示,现在考虑了前 i i i个字符, i i i前面第一个与 s [ i ] s[i] s[i]不同的位置是 j j j, j j j前面第一个与 s [ i ] , s [ j ] s[i],s[j] s[i],s[j]都不同的位置是 k k k。则初始化 f [ 1 ] [ 0 ] [ − 1 ] = 3 f[1][0][-1]=3 f[1][0][−1]=3,转移如下:
{ f [ i + 1 ] [ j ] [ k ] + = f [ i ] [ j ] [ k ] ( s [ i + 1 ] = s [ i ] ) f [ i + 1 ] [ i ] [ j ] + = f [ i ] [ j ] [ k ] ( s [ i + 1 ] = s [ k ] ) f [ i + 1 ] [ i ] [ k ] + = f [ i ] [ j ] [ k ] ( s [ i + 1 ] = s [ j ] ) \begin{cases} f[i+1][j][k]+=f[i][j][k] & (s[i+1]=s[i])\\ f[i+1][i][j]+=f[i][j][k] & (s[i+1]=s[k])\\ f[i+1][i][k]+=f[i][j][k] & (s[i+1]=s[j]) \end{cases} ⎩⎪⎨⎪⎧f[i+1][j][k]+=f[i][j][k]f[i+1][i][j]+=f[i][j][k]f[i+1][i][k]+=f[i][j][k](s[i+1]=s[i])(s[i+1]=s[k])(s[i+1]=s[j])
限制则相当于是对某个 i i i限制了 j , k j,k j,k的取值范围。
这个过程等价于对于一个二维矩阵:
1)给第
i
i
i行的元素赋值。
2)将一个矩形外的元素全部清零。
观察发现:
1)一个元素至多被清零一次,因为我们每一次新赋值的元素在此前一定没有被清零过。
2)只要维护每一行、每一列的所有元素的和就可以快速得到第
i
i
i行每个元素要赋的值。
3)每一行没有被清零过的元素构成连续的一段区间,因为在这一行的元素被赋了值以后,我们一定不会再对这些元素进行赋值操作,所以在整个过程中每一行未被清零的元素总构成连续的区间。
故而维护每一行未被清零的区间的左右端点以及每一行每一列所有元素的和,就可以在 O ( n 2 ) O(n^2) O(n2)的时间内解决问题。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
/*
f(i,j,k) ->
f(i+1,j,k) s[i+1]=s[i]
f(i+1,i,k) s[i+1]=s[j]
f(i+1,i,j) s[i+1]=s[k]
*/
template <class T> void cmax(T &x,T y) { if(y>x) x=y; }
template <class T> void cmin(T &x,T y) { if(y<x) x=y; }
const int mod=1e9+7;
inline void Add(int &x,int y) { x+=y; if(x>=mod) x-=mod; }
inline void Dec(int &x,int y) { x-=y; if(x<0) x+=mod; }
//const int N=55;
//int f[N][N][N];
//int lx[N],rx[N],ly[N],ry[N];
//int mx[N][4],mi[N][4];
//int n,m;
//inline bool In(int x,int l,int r) { return x>=l&&x<=r; }
//int main() {
// int T; rd(T);
// while(T--) {
// rd(n),rd(m);
// for(int i=1;i<=n;++i) for(int x=1;x<=3;++x) mi[i][x]=i,mx[i][x]=-1;
// for(int i=1;i<=m;++i) {
// int l,r,x; rd(l),rd(r),rd(x);
// cmin(mi[r][x],l);
// cmax(mx[r][x],l);
// }
// int flg=1;
// for(int i=1;i<=n;++i) {
// rx[i]=mi[i][1]-1,lx[i]=mx[i][2];
// ry[i]=mi[i][2]-1,ly[i]=mx[i][3];
// if(rx[i]<lx[i]||ry[i]<ly[i]) flg=0;
// }
// if(!flg) {
// printf("0\n");
// continue;
// }
// memset(f,0,sizeof(f));
// f[1][1][0]=3;
// for(int i=2;i<=n;++i) {
// for(int j=0;j<i-1;++j)
// for(int k=-1;k<j;++k) if(f[i-1][j+1][k+1]) {
// int cur=f[i-1][j+1][k+1];
// Add(f[i][j+1][k+1],cur);
// Add(f[i][i][j+1],cur);
// Add(f[i][i][k+1],cur);
// }
// for(int j=0;j<i;++j)
// for(int k=-1;k<j;++k)
// if(!(In(j,lx[i],rx[i])&&In(k,ly[i],ry[i]))) f[i][j+1][k+1]=0;
// }
// int ans=0;
// for(int j=0;j<n;++j)
// for(int k=-1;k<j;++k) {
// Add(ans,f[n][j+1][k+1]);
// }
// printf("%d\n",ans);
// }
// return 0;
//}
const int N=5010;
int sx[N],sy[N];
int lx[N],rx[N],ly[N],ry[N];
int pl[N],pr[N];
int a[N][N];
inline void del(int x,int y) {
Dec(sx[x],a[x][y]);
Dec(sy[y],a[x][y]);
a[x][y]=0;
}
inline void cl(int x,int l,int r) {
if(l>r) {
for(int i=pl[x];i<=pr[x];++i) del(x,i);
pl[x]=1,pr[x]=0;
return;
}
for(int i=pr[x];i>r&&i>=pl[x];--i) del(x,i);
for(int i=pl[x];i<l&&i<=pr[x];++i) del(x,i);
pl[x]=max(pl[x],l);
pr[x]=min(pr[x],r);
}
int mx[N][4],mi[N][4];
int n,m;
int main() {
int T; rd(T);
while(T--) {
rd(n),rd(m);
for(int i=1;i<=n;++i) for(int x=1;x<=3;++x) mx[i][x]=-1,mi[i][x]=i;
for(int i=1;i<=m;++i) {
int l,r,x; rd(l),rd(r),rd(x);
cmax(mx[r][x],l);
cmin(mi[r][x],l);
}
int flg=1;
for(int i=1;i<=n;++i) {
lx[i]=mx[i][2],rx[i]=mi[i][1]-1;
ly[i]=mx[i][3],ry[i]=mi[i][2]-1;
if(lx[i]>rx[i]||ly[i]>ry[i]) flg=0;
}
if(!flg) {
printf("0\n");
continue;
}
for(int i=0;i<=n+1;++i) {
sx[i]=sy[i]=0;
for(int j=0;j<=n+1;++j) a[i][j]=0;
}
sx[1]=sy[0]=a[1][0]=3;
pl[1]=pr[1]=0;
for(int i=2;i<=n;++i) {
for(int j=ry[i];j>=ly[i];--j)
Add(a[i][j+1],(sx[j+1]+sy[j+1])%mod);
for(int j=ry[i];j>=ly[i];--j) {
Add(sx[i],a[i][j+1]);
Add(sy[j+1],a[i][j+1]);
}
pl[i]=ly[i]+1,pr[i]=ry[i]+1;
for(int j=-1;j<lx[i];++j) cl(j+1,1,0);
for(int j=lx[i];j<=rx[i];++j) cl(j+1,ly[i]+1,ry[i]+1);
for(int j=rx[i]+1;j<i;++j) cl(j+1,1,0);
for(int j=-1;j<i;++j) cl(j+1,0,j);
}
int ans=0;
for(int i=0;i<=n+1;++i) Add(ans,sx[i]);
printf("%d\n",ans);
}
return 0;
}
F - Substring Query
定义字符串 t t t是字符串 s s s的half border,当且仅当 t t t是 s s s的border且 ∣ t ∣ ⋅ 2 ≤ ∣ s ∣ |t| \cdot 2 \le |s| ∣t∣⋅2≤∣s∣。
对于一个字符串 s s s,定义 S ( l , r ) S(l,r) S(l,r)表示 s s s长度为 l − 1 l-1 l−1的前缀和长度为 ∣ s ∣ − r |s| - r ∣s∣−r的后缀拼接起来得到的字符串。
现在给出 s s s,有 q q q次询问,每次询问给出 l , r l,r l,r,你需要求出 S ( l , r ) S(l,r) S(l,r)所有half border的长度的和。
多组数据,保证 ∑ ∣ s ∣ , ∑ q ≤ 5 × 1 0 5 \sum|s|,\sum q \le 5\times 10^5 ∑∣s∣,∑q≤5×105
Sol
第一种情况是half border的长度不超过 min { l − 1 , ∣ s ∣ − r } \min \{ l-1,|s|-r\} min{l−1,∣s∣−r},直接kmp就可以了。
第二种情况是half border的长度超过了 min { l − 1 , ∣ s ∣ − r } \min \{ l-1,|s|-r\} min{l−1,∣s∣−r},假设 l − 1 > ∣ s ∣ − r l-1 > |s| - r l−1>∣s∣−r,则会有:
发现: A A A是 s [ 1 ⋯ l − 1 ] s[1\cdots l-1] s[1⋯l−1]的border; B B B是 s [ ∣ A ∣ + 1 ⋯ ∣ s ∣ ] s[|A|+1\cdots |s|] s[∣A∣+1⋯∣s∣]的border; ∣ A ∣ + ∣ B ∣ + ∣ A ∣ < L |A|+|B|+|A| < L ∣A∣+∣B∣+∣A∣<L。
也就是说在正串的border树上, s [ 1 ⋯ L − 1 ] s[1\cdots L-1] s[1⋯L−1]在 A A A的子树内;在反串的border树上, s [ ∣ A ∣ + 1 ⋯ ∣ s ∣ ] s[|A| + 1\cdots |s|] s[∣A∣+1⋯∣s∣]在 B B B的子树内。
可以转化成border树上的二维数点问题,具体细节见代码。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#define PB push_back
#define ll long long
using namespace std;
template <class T>
inline void rd(T &x) {
x=0; char c=getchar(); int f=1;
while(!isdigit(c)) { if(c=='-') f=-1; c=getchar(); }
while(isdigit(c)) x=x*10-'0'+c,c=getchar(); x*=f;
}
const int N=5e5+10;
char str[N];
int n;
void kmp(char *S,int *nxt) {
int p=0; nxt[1]=0,nxt[0]=-1;
for(int i=2;i<=n;++i) {
while(~p&&S[i]!=S[p+1]) p=nxt[p];
nxt[i]=++p;
}
}
struct item {
int c; ll s;
item(int c=0,ll s=0): c(c),s(s) {}
friend item operator +(item A,item B) { return item(A.c+B.c,A.s+B.s); }
friend item operator -(item A,item B) { return item(A.c-B.c,A.s-B.s); }
};
namespace BIT {
item c[N];
void init() { for(int i=1;i<=n+1;++i) c[i]=item(0,0); }
void add(int i,item t) { for(;i<=n+1;i+=i&-i) c[i]=c[i]+t; }
item query(ll i) { item ans=0; for(;i;i-=i&-i) ans=ans+c[i]; return ans; }
}
ll sum[N];
namespace TREE_B {
vector<int> son[N];
char S[N];
int nxt[N],dfn[N],sz[N],id;
void dfs(int u) {
sz[u]=1,dfn[u]=++id;
for(int i=0;i<son[u].size();++i)
dfs(son[u][i]),sz[u]+=sz[son[u][i]];
}
void Debug(int u) {
printf("%d ",u);
for(int i=0;i<son[u].size();++i) Debug(son[u][i]);
}
void main() {
for(int i=1;i<=n;++i) S[i]=str[n-i+1];
kmp(S,nxt);
for(int i=1;i<=n;++i) son[n-nxt[i]+1].PB(n-i+1);
dfs(n+1);
id=0; for(int i=1;i<=n+1;++i) son[i].clear();
}
inline int QL(int x) { return dfn[x]; }
inline int QR(int x) { return dfn[x]+sz[x]-1; }
}
using TREE_B::QL;
using TREE_B::QR;
struct Wry {
int id,r;
Wry(int id=0,int r=0): id(id),r(r) {}
};
vector<Wry> Q[N];
ll Qans[N];
int q;
struct seg {
int l,r;
seg(int l=0,int r=0): l(l),r(r) {}
}G[N];
namespace TREE_A {
vector<int> son[N];
int nxt[N];
int p[N][20];
void dfs1(int u) {
p[u][0]=u?nxt[u]:0;
for(int j=1;j<=19;++j) p[u][j]=p[p[u][j-1]][j-1];
for(int i=0;i<son[u].size();++i) dfs1(son[u][i]);
}
void dfs2(int u) {
if(u) BIT::add(QL(u+1),item(1,u));
for(int i=0;i<Q[u].size();++i) {
item tmp=BIT::query(QR(Q[u][i].r))-BIT::query(QL(Q[u][i].r)-1);
Qans[Q[u][i].id]+=tmp.s+(n-Q[u][i].r+1)*(ll)tmp.c;
}
for(int i=0;i<son[u].size();++i)
dfs2(son[u][i]);
if(u) BIT::add(QL(u+1),item(-1,-u));
}
void main() {
kmp(str,nxt);
for(int i=1;i<=n;++i) son[nxt[i]].PB(i);
dfs1(0);
for(int i=1;i<=q;++i) {
int u=G[i].l,len=(u-(n-G[i].r+1))/2;
for(int j=19;j>=0;--j)
if(p[u][j]>len) u=p[u][j];
if(u) Q[p[u][0]].PB(Wry(i,G[i].r));
}
dfs2(0);
for(int i=0;i<=n;++i) son[i].clear(),Q[i].clear();
}
ll sum[N];
void sol() {
int cur=n;
while(cur) sum[cur]+=cur,cur=nxt[cur];
for(int i=1;i<=n;++i) sum[i]+=sum[i-1];
for(int i=1;i<=q;++i) Qans[i]+=sum[min(n-G[i].r+1,G[i].l)];
for(int i=1;i<=n;++i) sum[i]=0;
}
}
void sol() {
TREE_B::main();
TREE_A::main();
BIT::init();
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int T; rd(T);
while(T--) {
rd(n),rd(q);
scanf("%s",str+1);
for(int i=1;i<=q;++i) rd(G[i].l),rd(G[i].r),G[i].l--,G[i].r++;
sol();
TREE_A::sol();
reverse(str+1,str+n+1);
for(int i=1;i<=q;++i) {
G[i].l=n-G[i].l+1;
G[i].r=n-G[i].r+1;
swap(G[i].l,G[i].r);
}
sol();
for(int i=1;i<=q;++i) printf("%lld\n",Qans[i]);
for(int i=1;i<=q;++i) Qans[i]=0;
for(int i=1;i<=n;++i) Q[i].clear();
}
return 0;
}
(n),rd(q);
scanf("%s",str+1);
for(int i=1;i<=q;++i) rd(G[i].l),rd(G[i].r),G[i].l--,G[i].r++;
sol();
TREE_A::sol();
reverse(str+1,str+n+1);
for(int i=1;i<=q;++i) {
G[i].l=n-G[i].l+1;
G[i].r=n-G[i].r+1;
swap(G[i].l,G[i].r);
}
sol();
for(int i=1;i<=q;++i) printf("%lld\n",Qans[i]);
for(int i=1;i<=q;++i) Qans[i]=0;
for(int i=1;i<=n;++i) Q[i].clear();
}
return 0;
}