小 C C C有一个括号序列 A A A,其长度为 m m m,且序列元素只包含左右括号。他想生成一个无限长的括号序列 B B B,由于 B B B的长度为正无穷,所以其下标可以为任意整数(可以为负)。为了由 A A A生成 B B B,小 C C C采用如下方式:
{ b i = a i , 0 ≤ i < n b i = b i − n , i ≥ n b i = b i + n , i < 0 \begin{cases} b_i=a_i,\qquad 0\leq i
无聊的小 C C C还打算以序列 B B B为基础生成无穷个长度为正无穷的括号序列,我们定义 B k B^k Bk代表第 k k k个无穷序列, B 0 = B B^0=B B0=B。对于任意 k ≥ 1 k\geq 1 k≥1,由 B k − 1 B^{k-1} Bk−1生成 B k B^k Bk的方式如下:
{ b i k = b i + 1 k − 1 , b i k − 1 = ‘ ( ’ b i k = b i − 1 k − 1 , b i k − 1 = ‘ ) ’ \begin{cases} b_i^k=b_{i+1}^{k-1}, \quad b_i^{k-1}=‘(’ \\ b_i^k=b_{i-1}^{k-1}, \quad b_i^{k-1}=‘)’ \end{cases} {bik=bi+1k−1,bik−1=‘(’bik=bi−1k−1,bik−1=‘)’
最后,小 C C C有 q q q次询问,每次询问给定的 k , l , r k,l,r k,l,r,求有多少个左括号存在于无穷括号序列 B k B^k Bk中下标位于 [ l , r ] [l,r] [l,r]的元素中。
有 T T T组数据。
1 ≤ n , q ≤ 1 0 5 , 0 ≤ k ≤ 1 0 9 , − 1 0 9 ≤ l ≤ r ≤ 1 0 9 , 1 ≤ T ≤ 10 1\leq n,q\leq 10^5,0\leq k\leq 10^9,-10^9\leq l\leq r\leq 10^9,1\leq T\leq 10 1≤n,q≤105,0≤k≤109,−109≤l≤r≤109,1≤T≤10
时间限制 3000 m s 3000ms 3000ms,空间限制 512 M B 512MB 512MB。
设 f ( k , i ) f(k,i) f(k,i)表示 B k B^k Bk中第 i i i个左括号的下标( i i i可以为 0 0 0甚至负数),设 g ( k , p ) g(k,p) g(k,p)表示满足 f ( k , i ) ≤ p f(k,i)\leq p f(k,i)≤p的最大的 i i i,则区间 [ l , r ] [l,r] [l,r]里的左括号的总数为 g ( k , r ) − g ( k , l − 1 ) g(k,r)-g(k,l-1) g(k,r)−g(k,l−1)。 g g g的值可以用二分求出。
因为我们只关心 g g g的差值,所以第 0 0 0个左括号的位置对应那个左括号其实是可以任意指定的。方便起见,我们把括号序列 A A A中的第一个左括号设为第 0 0 0个左括号。
下面考虑 f ( k , i ) f(k,i) f(k,i)的转移式:
那么,转移式为
f ( k , i ) = min { f ( k − 1 , i ) + 1 , f ( k − 1 , i + 1 ) − 1 } f(k,i)=\min\{f(k-1,i)+1,f(k-1,i+1)-1\} f(k,i)=min{f(k−1,i)+1,f(k−1,i+1)−1}
假设 f ( k , i ) f(k,i) f(k,i)是从 f ( 0 , j ) f(0,j) f(0,j)转移过来的,那么第一维从 0 0 0走到 k k k的过程中, min \min min中的第二项恰好选了 j − i j-i j−i次,第一项恰好选了 k − ( j − i ) k-(j-i) k−(j−i)次,所以转移式还可以写为
f ( k , i ) = min i ≤ j ≤ i + k { f ( 0 , j ) + k − 2 ( j − i ) } f(k,i)=\min\limits_{i\leq j\leq i+k}\{f(0,j)+k-2(j-i)\} f(k,i)=i≤j≤i+kmin{f(0,j)+k−2(j−i)}
可以看出决策区间的大小为 k k k。设括号序列 A A A中,左括号的数量为 m m m,我们先分析 k ≤ m k\leq m k≤m时如何求解。
设 F ( j ) = f ( 0 , j ) − 2 j F(j)=f(0,j)-2j F(j)=f(0,j)−2j,注意到 f ( 0 , j + m ) − f ( 0 , j ) = n f(0,j+m)-f(0,j)=n f(0,j+m)−f(0,j)=n,则有
F ( j + t ) = F ( j % m + t ) + ( n − 2 m ) × ⌊ j m ⌋ F(j+t)=F(j\% m+t)+(n-2m)\times \lfloor\dfrac jm\rfloor F(j+t)=F(j%m+t)+(n−2m)×⌊mj⌋
其中 t t t是任意非负整数。我们可以用 R M Q RMQ RMQ来预处理 F ( 0 ) F(0) F(0)到 F ( 2 m − 1 ) F(2m-1) F(2m−1),然后把 [ i , i + k ] [i,i+k] [i,i+k]映射到 [ i % m , i % m + k ] [i\%m,i\%m+k] [i%m,i%m+k]求最小值即可。
接下来分析一下 k k k比较大的解法。讨论 n − 2 × m n-2\times m n−2×m的值:
这样,我们就把区间长度缩小到了 m m m,通过与上面类似的方法,用 R M Q RMQ RMQ求最小值即可。
时间复杂度为 O ( ∑ ( n log n + q log v ) ) O(\sum(n\log n+q\log v)) O(∑(nlogn+qlogv)),其中 v v v表示 l , r l,r l,r的值域。
可以参考代码帮助理解。
如果你 TLE \text{TLE} TLE的话,可以参考一下下面这些卡常小技巧:
#include
#define rg register
using namespace std;
const int N=100000;
const long long inf=1e9;
int T,n,m,q,lg[2*N+5],f[2*N+5],st[2*N+5][20];
char s[N+5];
int rd(){
int t=0,fl=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') fl=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
t=t*10+ch-'0';
ch=getchar();
}
return t*fl;
}
void init(){
lg[0]=-1;
for(rg int i=1;i<=2*N;i++) lg[i]=lg[i/2]+1;
}
void solve(){
for(rg int i=0;i<m;i++) f[i+m]=f[i]+n;
for(rg int i=0;i<2*m;i++) st[i][0]=f[i]-2*i;
for(rg int j=1;j<=17;j++){
for(rg int i=0;i+(1<<j)-1<2*m;i++){
st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
}
}
}
int findst(int l,int r){
int k=lg[r-l+1];
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int sv(int x,int mod){
if(x>=0) return x%mod;
return (x+inf*mod)%mod;
}
int find(int k,int x){
if(k<=m){
int md=sv(x,m),tmp=(x-md)/m;
return findst(md,md+k)+(n-2*m)*tmp+k+2*x;
}
else if(n-2*m>=0){
int md=sv(x,m),tmp=(x-md)/m;
return findst(md,md+m-1)+(n-2*m)*tmp+k+2*x;
}
else{
int md=sv(x+k-m,m),tmp=(x+k-m-md)/m;
return findst(md+1,md+m)+(n-2*m)*tmp+k+2*x;
}
}
int gt(int k,int x){
int l=-inf,r=inf,mid;
while(l<=r){
mid=l+r>>1;
if(find(k,mid)<=x) l=mid+1;
else r=mid-1;
}
return l-1;
}
int main()
{
// freopen("seq.in","r",stdin);
// freopen("seq.out","w",stdout);
init();
T=rd();
while(T--){
scanf("%s",s);
n=strlen(s);m=0;
for(rg int i=0;i<n;i++){
if(s[i]=='(') f[m++]=i;
}
q=rd();
if(!m){
for(rg int i=1,k,l,r;i<=q;i++){
k=rd();l=rd();r=rd();
printf("0\n");
}
continue;
}
solve();
for(rg int i=1,k,l,r;i<=q;i++){
k=rd();l=rd();r=rd();
printf("%d\n",gt(k,r)-gt(k,l-1));
}
}
return 0;
}