(第一种做法)把操作序列中的所有值排个序。然后比如有 {2,3,5,10},若两个值之间不是相邻的,我们便加上一个点,管理这一段区间。复杂度
O
(
3
q
log
(
3
q
)
)
O(3q\log (3q))
O(3qlog(3q)),然后就
T
L
E
\color{red}{TLE}
TLE 了
(第二种做法)我们令操作序列变为左闭右开。也就是对于区间置
[
L
,
R
]
[L,R]
[L,R],我们相当于变成区间置
[
L
,
R
+
1
)
[L,R+1)
[L,R+1)。这样,离散化之后就只多加了两个点:
L
,
R
+
1
L,R+1
L,R+1。排序后,我们每个点相当于掌管了
M
[
i
+
1
]
−
M
[
i
]
M[i+1]-M[i]
M[i+1]−M[i] 个点。 区间置,只要让右端点的离散化下标
−
1
-1
−1即可。
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definels(p<<1)#definers(p<<1|1)#definemd((l+r)>>1)#definelllonglongconstint MAX =6e5+50;int cnt[MAX];int sum[MAX*4];int shu[MAX*4];int tag[MAX*4];inlinevoidpush_up(int p){
sum[p]=sum[ls]+sum[rs];}inlinevoidbuild(int p,int l,int r){
tag[p]=-1;if(l == r){
sum[p]= shu[p]= cnt[l];return;}build(ls,l,md);build(rs,md+1,r);
shu[p]= shu[ls]+ shu[rs];push_up(p);}inlinevoidadd(int p,int l,int r,int k){
tag[p]= k;
sum[p]= k * shu[p];}inlinevoidpush_down(int p,int l,int r){if(~tag[p]){add(ls,l,md,tag[p]);add(rs,md+1,r,tag[p]);
tag[p]=-1;}}inlinevoidupdate(int p,int l,int r,int ux,int uy,int k){if(ux <= l && uy >= r){add(p,l,r,k);return;}push_down(p,l,r);if(ux <= md)update(ls,l,md,ux,uy,k);if(uy > md)update(rs,md+1,r,ux,uy,k);push_up(p);}structnode{int l,r,k;}aa[MAX];int M[MAX];
map<int,int>M2;intmain(){int n,q;
n =read();q =read();int ci =0;for(int i =1;i <= q;++i){
aa[i].l =read();
aa[i].r =read()+1;
aa[i].k =read()-1;
M[++ci]= aa[i].l;
M[++ci]= aa[i].r;}
M[++ci]=1;
M[++ci]= n +1;sort(M+1,M+ci+1);int m =unique(M+1,M+ci+1)-(M+1);for(int i =1;i <= m;++i){
M2[M[i]]= i;if(i != m)cnt[i]= M[i+1]- M[i];}build(1,1,m-1);for(int i =1;i <= q;++i){update(1,1,m-1,M2[aa[i].l],M2[aa[i].r]-1,aa[i].k);Print(sum[1],'\n');}Write();return0;}
思路二:珂朵莉树
嗯?全是区间置?那不是珂朵莉狂喜?直角交一发裸的珂朵莉:
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definels(p<<1)#definers(p<<1|1)#definemd((l+r)>>1)#definelllonglongconstint MAX =6e5+50;structnode{int l,r;
mutable int val;
bool operator<(const node &a)const{return l<a.l;}node(int L,int R,ll Val):l(L),r(R),val(Val){}node(int L):l(L){}};
set<node> s;
using si = set<node>::iterator;
si split(int pos){
si it = s.lower_bound(node(pos));if(it != s.end()&& it->l==pos)return it;--it;int l=it->l,r=it->r;
ll val = it->val;
s.erase(it);
s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;}voidassign(int l,int r,int val){
si itr=split(r+1),itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,val));}voidadd(int l,int r,int val){
si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)
it->val += val;}intquery(int l,int r){
si itr=split(r+1),itl=split(l);intres(0);for(si it=itl;it!=itr;++it)
res=res+(it->r-it->l+1)* it->val;return res;}int lef[MAX],rr[MAX],kk[MAX];int tmp[MAX];intmain(){int n,q;
n =read();q =read();
s.insert((node){1,n,1});for(int i =1;i <= q;++i){
lef[i]=read();rr[i]=read();kk[i]=read();assign(lef[i],rr[i],kk[i]-1);Print(query(1,n),'\n');}Write();return0;}
但是结果是
T
L
E
30
\color{red}{TLE\ 30}
TLE30 考虑到,我们每次求和都是求和所有位置的元素和。但是我们操作只操作了一部分,也就是说根据我们操作的这一段改变了多少值,我们直接就知道所有位置元素和的变化值。
代码:
300
M
s
300Ms
300Ms
时间复杂度:
O
(
q
log
q
)
O(q\log q)
O(qlogq)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definels(p<<1)#definers(p<<1|1)#definemd((l+r)>>1)#definelllonglongconstint MAX =6e5+50;int sum;structnode{int l,r;
mutable int val;
bool operator<(const node &a)const{return l<a.l;}node(int L,int R,ll Val):l(L),r(R),val(Val){}node(int L):l(L){}};
set<node> s;
using si = set<node>::iterator;
si split(int pos){
si it = s.lower_bound(node(pos));if(it != s.end()&& it->l==pos)return it;--it;int l=it->l,r=it->r;
ll val = it->val;
s.erase(it);
s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;}voidassign(int l,int r,int val){
si itr=split(r+1),itl=split(l);for(si i = itl;i != itr;++i)sum -= i->val *(i->r - i->l+1);// 直接修改 sum
s.erase(itl,itr);
s.insert(node(l,r,val));
sum += val *(r - l +1);}voidadd(int l,int r,int val){
si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)
it->val += val;}intquery(int l,int r){
si itr=split(r+1),itl=split(l);intres(0);for(si it=itl;it!=itr;++it)
res=res+(it->r-it->l+1)* it->val;return res;}int lef[MAX],rr[MAX],kk[MAX];int tmp[MAX];intmain(){int n,q;
n =read();q =read();
s.insert((node){1,n,1});
sum = n;for(int i =1;i <= q;++i){
lef[i]=read();rr[i]=read();kk[i]=read();assign(lef[i],rr[i],kk[i]-1);Print(sum,'\n');}Write();return0;}
C:Team Building | CF1316E
题意
Team Building 有
n
n
n 个人,你要选
p
p
p 个人,每个人成为
p
p
p 个位置之一,你还要选
k
k
k 个人成为观众 现在给定了
b
i
,
j
b_{i,j}
bi,j 表示第
i
i
i 个人担任第
j
j
j 个位置的收益,和
a
i
a_i
ai 表示第
i
i
i 个人担任观众的收益 求最大收益
2
≤
n
≤
1
0
5
2\le n\le 10^5
2≤n≤105
1
≤
p
≤
7
1\le p\le 7
1≤p≤7
1
≤
p
,
p
+
k
≤
n
1\le p,p+k\le n
1≤p,p+k≤n
1
≤
a
i
,
b
i
,
j
≤
1
0
9
1\le a_i,b_{i,j}\le 10^9
1≤ai,bi,j≤109
思路:贪心 + 状压DP
如果
p
=
1
p=1
p=1,且两个位置没有选用人的上限,那么自然根据贪心,每个人哪个位置优选哪个
如果
p
=
1
p=1
p=1,且第一个位置只能选一个人,第二个位置没有选用人的上限,那么记录
d
p
[
i
]
[
0
/
1
]
dp[i][0/1]
dp[i][0/1] 表示选完了前
i
i
i 个人,且选了 / 没有选第一个位置的人,最大收益是多少
如果
p
=
1
p=1
p=1,且第一个位置只能选一个人,第二个位置只能选
k
k
k 个人。那该怎么办好呢? 我们可以简单地想到。如果选定了某个人当第一个位置,那么剩下的所有人可以根据第二个位置的权值进行排序,然后贪心地选前
k
k
k 个当做答案。 但是这么做肯定会
T
T
T 掉,考虑如何加速或者转变这个过程。我们可以先按照第二个位置的值降序排序。 那么我们可以根据现在的位置
i
i
i 和到底有没有选第一个位置
0
/
1
0/1
0/1 进行判断,该人的第二个位置的值是否应该加上。
那么思路就很清晰了。考虑
p
≤
7
p\le 7
p≤7,我们设一个
d
p
[
i
]
[
2
7
]
dp[i][2^7]
dp[i][27] ,表示考虑完前
i
i
i 个人,且前
p
p
p 个位置选择的情况为
S
S
S。这样,根据
i
−
p
o
p
c
n
t
(
S
)
i-popcnt(S)
i−popcnt(S) 就可以判断这个人是否应该选为观众。 注意这样内存可能会比较大,滚动数组一下即可。
代码
时间复杂度:
O
(
p
n
2
p
+
n
log
n
)
O(pn2^p+n\log n)
O(pn2p+nlogn)
Asterism (Hard Version) 给你一个长度为
n
n
n 的数组
a
n
a_n
an 和一个质数
p
p
p 假设你一开始有数字
x
x
x。假设选择了
a
n
a_n
an 的一个排列
b
n
b_n
bn 然后一个位置一个位置过去比较。如果
x
≥
b
i
x\ge b_i
x≥bi,你就赢了他,并且你的
x
=
x
+
1
x=x+1
x=x+1 否则,你就输了
令
f
(
x
)
f(x)
f(x) 表示你一开始有数字
x
x
x,有多少个全排列
b
n
b_n
bn 满足你能赢所有人。 如果
p
∤
f
(
x
)
p\not| f(x)
p∣f(x),那么说明
f
(
x
)
f(x)
f(x) 是好的。 请输出所有好的的
f
(
x
)
f(x)
f(x)
2
≤
p
≤
n
≤
1
0
5
2\le p\le n\le 10^5
2≤p≤n≤105
1
≤
a
i
≤
1
0
9
1\le a_i\le 10^9
1≤ai≤109
思路
首先令
m
x
=
max
{
a
i
}
mx=\max\{a_i\}
mx=max{ai} 容易得到合法的
x
x
x 必须要满足
x
∈
[
m
x
−
n
+
1
,
m
x
−
1
]
x\in[mx-n+1,mx-1]
x∈[mx−n+1,mx−1] 因为若
x
≥
m
x
x\ge mx
x≥mx,那么容易得到
f
(
x
)
=
n
!
f(x)=n!
f(x)=n!,肯定有
p
∣
n
!
p|n!
p∣n! 因为
p
≤
n
p\le n
p≤n
我们令
c
i
c_i
ci 表示小于等于
i
i
i 的数的个数。 首先,我们的分数为
x
x
x,全排列的第一个位置的可选方案数有
c
x
c_x
cx 然后,我们赢了,第二个位置的可选方案数有
c
x
+
1
−
1
c_{x+1}-1
cx+1−1 以此类推,我们得到:
f
(
x
)
=
∏
i
=
x
x
+
n
−
1
c
i
−
(
i
−
x
)
f(x)=\prod_{i=x}^{x+n-1}c_i-(i-x)
f(x)=i=x∏x+n−1ci−(i−x) 这个式子可以通过
e
a
s
y
easy
easy 版的复杂度,但是这里仍然不行。
考虑到,我们必须要枚举所有的
f
(
x
)
,
x
∈
[
m
x
−
n
+
1
,
m
x
−
1
]
f(x),x\in[mx-n+1,mx-1]
f(x),x∈[mx−n+1,mx−1],所以每个
f
(
x
)
f(x)
f(x) 的判别需快速判断是否是
p
p
p 的倍数,即是否存在因子
p
p
p 由于
p
p
p 是一个质数。若
c
i
−
(
i
−
x
)
c_i-(i-x)
ci−(i−x) 是
p
p
p 的倍数,那么
f
(
x
)
f(x)
f(x) 就是
p
p
p 的倍数了。 即
c
i
−
(
i
−
x
)
≡
0
(
m
o
d
p
)
c_i-(i-x)\equiv 0\pmod p
ci−(i−x)≡0(modp) 即
x
≡
i
−
c
i
(
m
o
d
p
)
x\equiv i-c_i\pmod p
x≡i−ci(modp) 首先我们需要预处理好所有的
c
i
c_i
ci。注意到
a
i
a_i
ai 特别大,且
x
x
x 也是比较大的。 我们在同余式子两边同时减掉相同的数字,同余式子还是成立的。所以我们式子两边同时减去
m
x
−
n
mx-n
mx−n ,这样
c
i
c_i
ci 的范围变最大为
m
x
−
(
m
x
−
n
)
=
n
mx-(mx-n)=n
mx−(mx−n)=n,用一个桶即可。
然后对于第一个
f
(
x
)
f(x)
f(x),本来是
f
(
m
x
−
n
+
1
)
f(mx-n+1)
f(mx−n+1),偏移之后就变成了
f
(
1
)
f(1)
f(1),我们便统计所有
i
∈
[
1
,
n
]
i\in[1,n]
i∈[1,n],
i
−
c
i
i-c_i
i−ci 模
p
p
p 下的值是多少(注意这里
c
i
c_i
ci 提前偏移过了),用一个桶记录。
对于后面的
f
(
x
)
f(x)
f(x),容易发现就是增加了一个
(
x
+
n
)
−
c
x
+
n
(x+n)-c_{x+n}
(x+n)−cx+n,减少了一个
x
−
c
x
x-c_x
x−cx,简单递推即可
代码
时间复杂度:
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definelllonglongconstint MAX =2e5+50;constint MOD =1e9+7;const ll LINF =0x3f3f3f3f3f3f3f3f;intmod(int a,int p){return(a % p + p)% p;}intmain(){int n,p;
cin >> n >> p;
vector<int>aa(n),bb(2*n);for(int i =0;i < n;++i){
cin >> aa[i];}int mx =*max_element(aa.begin(),aa.end());for(int i =0;i < n;++i)
bb[max(0,aa[i]-(mx-n))]++;for(int i =1;i <2* n;++i)
bb[i]+= bb[i-1];
vector<int>ff(n);constint ORI = mx - n;for(int i =1;i <= n;++i)
ff[mod(i+ORI-bb[i],p)]++;
vector<int>res;for(int i =1;i < n;++i){if(ff[mod(i+ORI,p)]==0)res.push_back(i+ORI);
ff[mod(i+ORI-bb[i],p)]--;
ff[mod(n+i+ORI-bb[n+i],p)]++;}
cout << res.size()<< endl;for(auto it : res){
cout << it <<" ";}return0;}
E:Mike and Foam | CF547C
题意
Mike and Foam 一开始是一个空多重集合。每次添加进去一个元素或者从集合中删除一个元素。 每次操作完之后,询问有多少对元素
x
,
y
x,y
x,y,满足
gcd
(
x
,
y
)
=
1
\gcd(x,y)=1
gcd(x,y)=1 (对
x
,
y
x,y
x,y 与对
y
,
x
y,x
y,x 算同一对) 元素值
≤
5
⋅
1
0
5
\le 5\cdot 10^5
≤5⋅105 操作数
≤
2
⋅
1
0
5
\le 2\cdot10^5
≤2⋅105
思路:莫比乌斯反演
用直接的容斥,判断每个质因子是否相同,但是感觉挺复杂的… 我们直接使用莫比乌斯反演去做,更好理解
t
m
p
=
∑
i
=
1
n
∑
j
=
1
n
[
gcd
(
a
i
,
a
j
)
=
1
]
\begin{aligned} tmp&=\sum_{i=1}^n \sum_{j=1}^n [\gcd(a_i,a_j)=1] \end{aligned}
tmp=i=1∑nj=1∑n[gcd(ai,aj)=1]
但是这个
a
i
a_i
ai 感觉不是很好弄。弄下标不好弄,我们就弄值。 即
c
i
c_i
ci 表示有多少个数字的值为
i
i
i,记
n
=
max
{
a
i
}
n=\max\{a_i\}
n=max{ai},于是我们有:
t
m
p
=
∑
i
=
1
n
∑
j
=
1
n
[
gcd
(
i
,
j
)
=
1
]
×
c
i
c
j
=
∑
i
=
1
n
∑
j
=
1
n
∑
d
∣
gcd
(
i
,
j
)
μ
(
d
)
×
c
i
c
j
=
∑
d
=
1
n
μ
(
d
)
(
∑
i
=
1
⌊
n
i
⌋
c
i
d
)
2
\begin{aligned} tmp&=\sum_{i=1}^n \sum_{j=1}^n [\gcd(i,j)=1] \times c_ic_j\\ &=\sum_{i=1}^n \sum_{j=1}^n \sum_{d|\gcd(i,j)} \mu(d) \times c_ic_j\\ &=\sum_{d=1}^n\mu(d) \Big(\sum_{i=1}^{\lfloor \frac{n}{i}\rfloor}c_{id} \Big)^2\\ \end{aligned}
tmp=i=1∑nj=1∑n[gcd(i,j)=1]×cicj=i=1∑nj=1∑nd∣gcd(i,j)∑μ(d)×cicj=d=1∑nμ(d)(i=1∑⌊in⌋cid)2 我们记
f
(
d
)
=
∑
i
=
1
⌊
n
i
⌋
c
i
d
f(d)=\sum_{i=1}^{\lfloor \frac{n}{i}\rfloor}c_{id}
f(d)=i=1∑⌊in⌋cid 所以得到
t
m
p
=
∑
d
=
1
n
μ
(
d
)
f
2
(
d
)
tmp=\sum_{d=1}^n\mu(d) f^2(d)
tmp=∑d=1nμ(d)f2(d) 由于对称性,最后我们的答案为
a
n
s
=
(
t
m
p
−
c
1
)
/
2
ans=(tmp-c_1)/2
ans=(tmp−c1)/2
我们发现,在
x
≤
5
⋅
1
0
5
x\le 5\cdot 10^5
x≤5⋅105 范围之内,数字最多就只有
200
200
200 个因子。所以修改的次数不会太多。我们可以使用
O
(
n
log
n
)
O(n\log n)
O(nlogn) 的埃筛预处理好所有数字的因子。 每次只会修改一些
f
(
d
)
f(d)
f(d),根据是增还是删,直接去修改即可。
代码
时间复杂度:
O
(
q
log
n
)
O(q\log n)
O(qlogn)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definelllonglongconstint MAX =5e5+50;int cnt;int pri[MAX];
bool vis[MAX];
vector<int>V[MAX];voidshai(int n){for(int i =1;i <= n;++i){for(int j = i;j <= n;j+=i){
V[j].push_back(i);}}}int mu[MAX];voidMu(int n){
mu[1]=1;for(int i =2;i <= n;++i){if(!vis[i]){
pri[++cnt]= i;
mu[i]=-1;}for(int j =1;j <= cnt && i * pri[j]<= n;++j){
vis[i * pri[j]]=1;if(i % pri[j]==0){
mu[i * pri[j]]=0;break;}else{
mu[i * pri[j]]=-mu[i];}}}}int aa[MAX];int shu[MAX];
ll f[MAX];
bool you[MAX];intmain(){Mu(5e5);shai(5e5);int n,m;scanf("%d%d",&n,&m);for(int i =1;i <= n;++i)scanf("%d",&aa[i]);
ll ans =0;for(int i =1;i <= m;++i){int pos,t;scanf("%d",&pos);
t = aa[pos];for(auto it : V[t]){int d = t / it;
ans -= mu[d]* f[d]* f[d];if(!you[pos])f[d]++;else f[d]--;
ans += mu[d]* f[d]* f[d];}if(you[pos])shu[t]--;else shu[t]++;
you[pos]^=1;printf("%lld\n",(ans - shu[1])/2);}return0;}
F:Anton and School - 2 | CF785D
题意
Anton and School - 2 我们定义常规简单括号,就是若括号序列长度为
2
n
2n
2n,那么这个括号序列前长度为
n
n
n 的括号都是左括号,后面
n
n
n 个括号都是右括号,且括号非空。 给定一个长度为
n
n
n 的括号序列。问你有多少个子序列,满足子序列是常规简单括号。 答案取模
1
e
9
+
7
1e9+7
1e9+7
1
≤
n
≤
2
⋅
1
0
5
1\le n\le 2\cdot10^5
1≤n≤2⋅105
思路:组合数学,范德蒙德公式优化
首先,我们为了让枚举不重复,让所有左括号的最右边的位置在
i
i
i 处。 所以,在
1
∼
i
1\sim i
1∼i 处设有
a
a
a 个左括号,
i
+
1
∼
n
i+1\sim n
i+1∼n 处设有
b
b
b 个右括号。 那么这个时候,我们到底有多少种合法的方案呢? 首先初步的想法,就是我们括号序列长度为
2
i
2i
2i,即左边选择
i
i
i 个左括号,右边选择
i
i
i 个有括号,方案数即为:
∑
i
=
1
n
C
a
i
×
C
b
i
\sum_{i=1}^n C_a^i \times C_b^i
i=1∑nCai×Cbi 怎么化简呢?这不就是之前组合数学中学过的范德蒙德公式嘛,直接这样:
∑
i
=
1
∞
C
a
i
×
C
b
i
=
∑
i
=
1
∞
C
a
a
−
i
×
C
b
i
=
C
a
+
b
a
−
C
a
+
b
0
\begin{aligned} &\sum_{i=1}^\infin C_a^i \times C_b^i\\ &=\sum_{i=1}^\infin C_a^{a-i} \times C_b^i\\ &=C_{a+b}^a-C_{a+b}^0\\ \end{aligned}
i=1∑∞Cai×Cbi=i=1∑∞Caa−i×Cbi=Ca+ba−Ca+b0 但是我们没有考虑到,我们必须要选择最右边的左括号,也就是式子稍微变一下:
∑
i
=
1
∞
C
a
−
1
i
−
1
×
C
b
i
=
∑
i
=
1
∞
C
a
−
1
a
−
i
×
C
b
i
=
C
a
+
b
−
1
a
\begin{aligned} &\sum_{i=1}^\infin C_{a-1}^{i-1} \times C_b^i\\ &=\sum_{i=1}^\infin C_{a-1}^{a-i} \times C_b^i\\ &=C_{a+b-1}^a\\ \end{aligned}
i=1∑∞Ca−1i−1×Cbi=i=1∑∞Ca−1a−i×Cbi=Ca+b−1a
代码
时间复杂度:
O
(
n
)
O(n)
O(n)
#include<bits/stdc++.h>
using namespace std;voidshow(){std::cerr << endl;}template<typename T,typename... Args>voidshow(T x,Args... args){std::cerr <<"[ "<< x <<" ] , ";show(args...);}#definelllonglongconstint MAX =2e5+50;constint MOD =1e9+7;const ll LINF =0x3f3f3f3f3f3f3f3f;
ll qpow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a%MOD;a=a*a%MOD;n>>=1;}return res;}
ll qpow(ll a,ll n,ll p){a%=p;ll res =1LL;while(n){if(n&1)res=res*a%p;a=a*a%p;n>>=1;}return res;}
ll npow(ll a,ll n){/* */ll res =1LL;while(n){if(n&1)res=res*a;a=a*a;n>>=1;if(res<0||a<0)return0;}return res;}
ll inv(ll a){/* */returnqpow(a,MOD-2);}
ll inv(ll a,ll p){returnqpow(a,p-2,p);}
ll fac[MAX],ivfac[MAX];voidinit(int n){
fac[0]=1;for(int i =1;i <= n;++i)fac[i]= fac[i-1]* i % MOD;
ivfac[n]=inv(fac[n]);for(int i = n -1;~i;--i) ivfac[i]= ivfac[i+1]*(i+1)% MOD;}
ll C(int n,int m){if(m <0|| m > n)return0;return fac[n]* ivfac[m]% MOD * ivfac[n - m]% MOD;}int pre[MAX],suf[MAX];char ss[MAX];intmain(){init(200000);scanf("%s",ss+1);int ed =strlen(ss+1);for(int i =1;i <= ed;++i){
pre[i]= pre[i-1];if(ss[i]=='(')pre[i]++;}for(int i = ed;i >=1;--i){
suf[i]= suf[i+1];if(ss[i]==')')suf[i]++;}
ll ans =0;for(int i =1;i <= ed;++i){if(ss[i]!='('|| suf[i+1]==0)continue;
ans =(ans +C(pre[i]+suf[i+1]-1,pre[i]))% MOD;}printf("%lld",ans);return0;}