一、题目链接
二、题目大意
给一个长度为 n n n 且字符集为 { ′ ( ′ , ′ ) ′ } \{'(', ')'\} {′(′,′)′} 的环形字符串 s s s,下标从 0 0 0 开始标号. 还给出一个整数 q q q 和一个整数 k k k.
定义走一步:对于每一个 i ( 0 ≤ i ≤ n − 1 ) i \; (0 \leq i \leq n - 1) i(0≤i≤n−1),若 s [ i ] = = ′ ( ′ s[i] == '(' s[i]==′(′,则走一步到达 ( i − k + n ) % n (i - k + n) \% n (i−k+n)%n 的位置;若 s [ i ] = = ′ ) ′ s[i] == ')' s[i]==′)′,则走一步到达 ( i + k ) % n (i + k) \% n (i+k)%n 的位置.
定义 f ( i , d ) f(i, d) f(i,d) 表示从位置 i i i 出发,走恰好 d d d 步到达的位置.
一共有 q q q 次询问,每次询问给出三个整数 l , r , d l, r, d l,r,d,表示询问 ∑ i = l r f ( i , d ) \begin{aligned} \sum_{i = l}^{r}{f(i, d)} \end{aligned} i=l∑rf(i,d).
1 ≤ n , q ≤ 1 0 5 , 1 ≤ k ≤ n , 1 ≤ l ≤ r ≤ n , 1 ≤ d ≤ 1 0 9 1 \leq n, q \leq 10^5, 1 \leq k \leq n, 1 \leq l \leq r \leq n, 1 \leq d \leq 10^9 1≤n,q≤105,1≤k≤n,1≤l≤r≤n,1≤d≤109.
三、分析
一开始想的是倍增 d p dp dp,但对于一段区间来讲,走 1 1 1 步之后可能变得不连续,遂放弃…
不过我们可以观察到一点就是:对于一个左括号 ′ ( ′ '(' ′(′,它会一直向左走,直到它又返回原地或者遇到一个右括号从而在二元环里循环跳,右括号同理.
所以,我们首先统计出对于每个 i i i,它需要走多少步才能进入二元环状态,并记为 a [ i ] a[i] a[i].
因此对于一次询问 ( l , r , d ) (l, r, d) (l,r,d) 来讲,我们可以把区间 [ l , r ] [l, r] [l,r] 的数分为两类。一类是已经进入二元环状态,另一类是还未进入二元环状态.
由于有 q q q 次询问,直接做不好搞,我们可以把所有询问离线下来,按照 d d d 从小到大排序。这样处理之后,上述提到的两种状态个数前者单调增加,后者单调减少. 具体实现像扫描线那样,对 a [ i d [ i ] ] a[id[i]] a[id[i]] 从小到大排序后,对于当前的 d i d_i di,只需要把介于 ( d i − 1 , d i ] (d_{i - 1}, d_i] (di−1,di] 之间的 a [ i d [ i ] ] a[id[i]] a[id[i]] 置为二元环状态.
因此对于当前询问,答案等于区间 [ l , r ] [l, r] [l,r] 中走 d d d 步之后处于二元环状态的终点位置和 + 区间 [ l , r ] [l, r] [l,r] 中走 d d d 步之后不处于二元环状态的终点位置和.
对于前者,显然答案只与 d d d 的奇偶性有关. 也就是说,奇数 d d d 对应一个终点集合,偶数 d d d 对应另一个终点集合. 因此,我们可以用树状数组 c [ 0 ] [ i ] c[0][i] c[0][i] 维护偶数 d d d 对应的终点集合,树状数组 c [ 1 ] [ i ] c[1][i] c[1][i] 对应奇数 d d d 的终点集合. 对于新插入的每一个 a [ i d [ i ] ] a[id[i]] a[id[i]],单点更新一下两个树状数组. 对于查询操作,就相当于查询区间和.
对于后者需要查询区间 [ l , r ] [l, r] [l,r] 中走 d d d 步之后不处于二元环状态的终点位置和,记记移动距离 x = d k x = dk x=dk, c a l c ( p , x ) calc(p, x) calc(p,x) 表示前 p p p 个位置走 x x x 步处于非二元环状态的终点位置和,则答案为 c a l c ( r , x ) − c a l c ( l − 1 , x ) calc(r, x) - calc(l - 1, x) calc(r,x)−calc(l−1,x).
对于求解 c a l c ( p , x ) calc(p, x) calc(p,x), 先考虑右括号的影响,分两段考虑. 第一段是区间 [ 1 , n − x − 1 ] [1, n - x - 1] [1,n−x−1],这段区间的所有位置走 x x x 步之后的位移一定是不小于 0 0 0 的(位移为 x % n x \% n x%n),因此答案等于区间 [ 1 , n − x − 1 ] [1, n - x - 1] [1,n−x−1] 中走 x x x 步处于非二元环状态的原位置和 + ( x % n ) × (x \% n) \times (x%n)×区间 [ 1 , n − x − 1 ] [1, n - x - 1] [1,n−x−1] 中走 x x x 步处于非二元环状态的原位置的个数,可使用两个树状数组维护. 第二段是区间 [ n − x , p ] [n - x, p] [n−x,p],这段区间的所有位置走 x x x 步之后的位移一定是不大于 0 0 0 的(位移为 x % n − n x \% n - n x%n−n),因此答案等于区间 [ n − x , p ] [n - x, p] [n−x,p] 中走 x x x 步处于非二元环状态的原位置和 + ( x % n − n ) × (x \% n - n) \times (x%n−n)×区间 [ n − x , p ] [n - x, p] [n−x,p] 中走 x x x 步处于非二元环状态的原位置的个数,可使用两个树状数组维护. 左括号的影响同理,此处不再赘述.
四、代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
const int M = (int)1e5;
const int N = (int)1e5;
const int inf = 0x3f3f3f3f;
const double eps = 1e-6;
const ll mod = (ll)1e9 + 7;
int n, q, k;
char s[M + 5];
int nx[M + 5];
int a[M + 5];
int id[M + 5];
bool vis[M + 5];
int b[2][M + 5];
ll c[2][M + 5];
ll lft[M + 5], lftNum[M + 5];
ll rht[M + 5], rhtNum[M + 5];
struct qnode
{
int l, r, d, id;
bool operator<(const qnode& b)const
{
return d < b.d;
}
} v[M + 5];
ll ans[M + 5];
int dfs(int u)
{
if(vis[u])
{
if(~a[u]) return a[u];
else return inf;
}
vis[u] = 1;
if(nx[nx[u]] == u) return a[u] = 0;
else return a[u] = dfs(nx[u]) + 1;
}
bool cmp(int x, int y)
{
return a[x] < a[y];
}
inline int lowbit(int n)
{
return n&-n;
}
void add(ll tree[], int p, ll x)
{
++p;
while(p <= n)
{
tree[p] += x;
p += lowbit(p);
}
}
ll ask(ll tree[], int p)
{
++p;
ll ans = 0;
while(p)
{
ans += tree[p];
p -= lowbit(p);
}
return ans;
}
void change(int p)
{
if(s[p] == '(') add(lft, p, -p), add(lftNum, p, -1);
else if(s[p] == ')') add(rht, p, -p), add(rhtNum, p, -1);
add(c[0], p, b[0][p]), add(c[1], p, b[1][p]);
}
ll calc(int p, int x)
{
ll ans = 0;
int pos = min(n - x - 1, p);
ans += ask(rht, pos) + ask(rhtNum, pos) * x;
ans += ask(rht, p) + ask(rhtNum, p) * (x - n) - ask(rht, pos) - ask(rhtNum, pos) * (x - n);
x = n - x;
pos = min(n - x - 1, p);
ans += ask(lft, pos) + ask(lftNum, pos) * x;
ans += ask(lft, p) + ask(lftNum, p) * (x - n) - ask(lft, pos) - ask(lftNum, pos) * (x - n);
return ans;
}
void work()
{
while(~scanf("%d %d %d", &n, &q, &k))
{
for(int i = 0; i < n; ++i)
{
a[i] = -1;
id[i] = i;
vis[i] = 0;
c[0][i] = c[1][i] = 0;
lft[i] = lftNum[i] = 0;
rht[i] = rhtNum[i] = 0;
}
scanf("%s", s);
for(int i = 0; i < n; ++i)
{
if(s[i] == '(') nx[i] = (i - k + n) % n;
else if(s[i] == ')') nx[i] = (i + k) % n;
}
for(int i = 0; i < n; ++i) if(!vis[i]) dfs(i);
sort(id, id + n, cmp);
for(int i = 0, f; i < n; ++i)
{
if(s[i] == '(') add(lft, i, i), add(lftNum, i, 1), f = -1;
else if(s[i] == ')') add(rht, i, i), add(rhtNum, i, 1), f = 1;
if(a[i] < inf)
{
b[a[i] % 2][i] = (i + 1ll * f * a[i] * k % n + n) % n;
b[1 - a[i] % 2][i] = nx[b[a[i] % 2][i]];
}
}
for(int i = 0; i < q; ++i)
{
scanf("%d %d %d", &v[i].l, &v[i].r, &v[i].d);
v[i].id = i;
}
sort(v, v + q);
for(int i = 0, j = 0, l, r, d; i < q; ++i)
{
l = v[i].l, r = v[i].r, d = v[i].d;
while(j < n && a[id[j]] <= d) change(id[j++]);
ans[v[i].id] = ask(c[d % 2], r) - ask(c[d % 2], l - 1);
ans[v[i].id] += calc(r, 1ll * k * d % n) - calc(l - 1, 1ll * k * d % n);
}
for(int i = 0; i < q; ++i) printf("%lld\n", ans[i]);
}
}
int main()
{
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// ios::sync_with_stdio(0);
// cin.tie(0); cout.tie(0);
// init();
// int T; scanf("%d" ,&T);
// for(int ca = 1; ca <= T; ++ca)
// {
printf("Case %d: ", ca);
// work();
// }
work();
// cerr << 1.0 * clock() / CLOCKS_PER_SEC << "\n";
return 0;
}