1.线段树什么的最讨厌了
Description
小Y 最近学习了线段树,但是由于她的智商比较低,运用的还不是很熟练。于是小R 给了她一点练习题训练,其中有一道是这样的。
这是小R 写的线段树的一段建树代码:
只要调用 b u i l d t r e e ( 1 , 0 , n ) buildtree(1,0,n) buildtree(1,0,n) 就可以得到一颗线段树了。显然,一颗线段树一共有 O ( n ) O(n) O(n) 个节点,因为每一个节点都代表了一个不同的区间,所以线段树上一共出现了 O ( n ) O(n) O(n) 个不同的区间。
现在小R 给了你一个区间 [ l , r ] [l,r] [l,r],他想要你告诉他一个最小的 n n n使得区间 [ l , r ] [l,r] [l,r]出现在了用 b u i l d t r e e ( 1 , 0 , n ) buildtree(1,0,n) buildtree(1,0,n)建出来的线段树中。
Input
第一行输入一个正整数 T T T表示数据组数。
接下来 T T T行每行三个整数 L L L, R R R, l i m lim lim 表示一组询问,如果对于所有的 0 ≤ 0 \leq 0≤ n n n ≤ \leq ≤ l i m lim lim 都不存在满足条件的解,输出 − 1 -1 −1即可。
Output
对于每组询问输出一个答案。
Data Constraint
Solutions
可以发现 [ L , R ] [L, R] [L,R] 的父节点代表的区间至多有 4 4 4 种,分别为 [ L , 2 R − L ] , [ L , 2 R − L + 1 ] , [ 2 L − R − 2 , R ] , [ 2 L − R − 1 , R ] [L, 2R−L], [L, 2R−L+ 1], [2L−R − 2, R], [2L − R − 1, R] [L,2R−L],[L,2R−L+1],[2L−R−2,R],[2L−R−1,R],所以只需要暴力枚举这些搜下去就好了,当两端超出限制的时候退出。
可以发现,每一搜索一次, L R − L + 1 \frac{L}{R−L+1} R−L+1L 都至少除以 2 2 2,所以搜索的复杂度应该为 O ( ( L R − L + 1 ) 2 ) O((\frac{L}{R−L+1})^2) O((R−L+1L)2),这是可以在时间范围内跑出来的。
Code
#include <cstdio>
const int N = 2e9 + 10;
int ans,L,R,lim;
void dfs(int l,int r)
{
if (r > lim) return;
if (r >= ans) return;
if (l == 0)
{
ans = r;
return;
}
int cnt = r - l + 1;
if (l - cnt == 0 || l - cnt >= cnt + cnt) dfs(l - cnt,r);
if (l - cnt == 1 || l - cnt - 1 >= cnt + cnt + 1)
dfs(l - cnt - 1,r);
if (l >= cnt + cnt - 1) dfs(l,r + cnt - 1);
if (l > cnt + cnt) dfs(l,r + cnt);
}
int main()
{
int T;
scanf("%d",&T);
while (T --)
{
scanf("%d%d%d",&L,&R,&lim);
ans = N;
dfs(L,R);
if (ans == N) printf("-1\n");
else printf("%d\n",ans);
}
}
2.已经没有什么好害怕的了
Description
小Y 最近开始学习算法姿势,但是因为小R 非常BB,给了她很多B6 题,所以她觉得自己已经没有什么前途了。于是小R 给了她一些稍微简单的题,让她觉得已经没有什么好害怕的了,其中一道是这样的:
给定一个长度为n 只包含左括号和右括号的序列,现在小R 想要知道经过每一个位置的合法子串有多少个。
空串是一个合法的串,如果A 和B 都是合法的串,那么(A) 和AB 都是合法的串。
Input
第一行输入一个正整数T 表示数据组数。接下来T 行每行一个字符串。
Output
对于每组数据,输出一个整数表示答案,令ansi 为经过第i 个位置的子串个数,那么你需要输出 ∑ i = 1 n ( i ∗ a n s i m o d    ( 1 e 9 + 7 ) ) \sum^{n}_{i=1}(i*ans_i\mod (1e9+7)) ∑i=1n(i∗ansimod(1e9+7))(注意是先求余再求和)
Data Constraint
对于 10 10% 10的数据, n < = 100 n<=100 n<=100
对于 30 30% 30 的数据, n < = 1000 n <= 1000 n<=1000
对于 60 60% 60的数据, n < = 5 < = 1 0 4 n <= 5 <= 10^4 n<=5<=104
对于 100 100% 100的数据, n < = 1 0 6 , 1 < = T < = 10 n <= 10^6,1 <= T<= 10 n<=106,1<=T<=10
Solutions
又是差分的题目,非常坑先给括号配对,让后记一对合法的括号为 [ l , r ) [l,r) [l,r)我们分别按顺序和倒序处理每对括号 [ l , r ) [l,r) [l,r),对于括号 [ l , r ) [l,r) [l,r),我们先对区间 [ l , r ) [l,r) [l,r)加上 1 1 1,让后考虑它的影响若 l l l是另一对括号 [ l ′ , r ′ ) [l',r') [l′,r′)的右端点(r’=l)那么显然这里的 [ l , r ) [l,r) [l,r)贡献可以全部加过去,若 r r r是另一对括号 [ l ′ , r ′ ) [l',r') [l′,r′)的左端点,那么显然也可以把 [ l , r ) [l,r) [l,r)的贡献全加过去,不合法的括号位置会自动抵消
Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000010
#define M 1000000007
using namespace std;
long long ans,f[N]; char s[N];
int n,m,t,w[N],l[N],r[N],cl[N],cr[N];
int _18520(){
scanf("%s",s+1); n=strlen(s+1);
for(int i=1;i<=n;++i)
if(s[i]=='(') w[++t]=i;
else if(t){
r[w[t]]=i+1; l[i+1]=w[t--]; }
for(int i=n+1;i;--i) cr[l[i]]+=++cr[i];
for(int i=1;i<=n;++i) cl[r[i]]+=--cl[i];
for(int i=1;i<=n;++i) f[i]=f[i-1]+cl[i]+cr[i];
for(int i=1;i<=n;++i) ans=ans+i*f[i]%M;
printf("%lld\n",ans);
}
int main(){
int T;
for(scanf("%d",&T);T--;_18520()){
memset(f,0,N<<3);
memset(l,0,N<<2);
memset(r,0,N<<2);
memset(cl,0,N<<2);
memset(cr,0,N<<2);
t=ans=0;
}
}
3.我才不是萝莉控呢
Description
小Y:“小R 你是萝莉控吗。”小R:“…”
为了避免这个尴尬的话题,小R 决定给小Y 做一道题。
有一个长度为 n n n 的正整数数组A,满足 A i > = A i + 1 Ai >= Ai+1 Ai>=Ai+1,现在构造一个数组B,令 B i Bi Bi= ∑ n i a \sum\limits^{i}_{n}a n∑ia
现在,有一个