test20231029
T1
经典题目加上一点变形。
如果说不是序列而是询问有多少个子串,那么有一个非常经典的方程:
令 \(f_{i,j}\) 为到第 \(i\) 位,前面有 \(j\) 个多的 (,
$f_{i,j}=f_{i,j}+f_{i-1,j-1} \quad $ \(s_i=\) (||?。
$f_{i,j}=f_{i,j}+f_{i-1,j+1} \quad $ \(s_i=\) )||?。
那么,如果是询问子序列呢,想一想,其实只不过是多了一种转移:
\(f_{i,j}=f_{i-1,j}\)
那么这道题就解决了。
int f[5050][5050];
char s[N];
int n;
int sum[5050][5050];
signed main(){
freopen("bracket.in", "r", stdin);
freopen("bracket.out", "w", stdout);
n=read();
scanf("%s",s+1);
sum[0][0]=1;f[0][0]=1;
sum[1][0]=1;f[1][0]=1;
up(i,1,n){
up(j,0,n){
f[i][j]=f[i-1][j];
if(j!=0&&(s[i]=='('||s[i]=='?')){
f[i][j]=(sum[i-1][j-1]+f[i][j])%mod;
}
if((s[i]==')'||s[i]=='?')){
f[i][j]=(sum[i-1][j+1]+f[i][j])%mod;
}
sum[i][j]=f[i][j]%mod;
}
}
cout<<sum[n][0];
return 0;
}
T2
蒸蒸日上。
注意到 \(m\) 十分的大, 如果把所有武将都全部升级肯定最优,因为每个武将都可以产生大于升级它所需代价的收益。
正着算不是很好算,我们可以将全部收益减去升级之前的收益,也就是说如果一个装置在第 \(j\) 分钟成功升了一级,那么就从总收益中减去 \(j\) 的收益。
题意转化为 \(n\) 个数组 \(a_i\),要将所有元素重排进 长度为 \(\sum k_i\) 数组 \(b\),使得 \(b\) 数组中的元素顺序满足在 \(a_i\) 数组中的顺序关系,并且使得 \(b\) 数组的前缀和的和最小。
如果不考虑 \(a\) 数组的限制关系,显然是直接将所有数排个序,可以使前缀和的和最小。
更一般的,我们设想 \(b\) 数组中的两段数,第一段数排在第二段前的充要条件,显然是第一段数的平均值小于第二段数。
所以我们要做的,是每次把所有 \(a_i\) 中最小平均值的一段数拿出来,放到 \(b\) 数组中去。
将每个 \(a\) 数组提前分好段,这样的段在每个 \(a\) 数组中的平均值都是单调递增的,就转化为所有元素都递增的的情况,直接排序就好了。
怎么分段呢?我们每次在当前段加入一个数,如果这一段平均值变小,就合并,否则继续。但这样的数列 \(9,8,7,6,10,1,1,1\) 前四个数的平均值大于后四个数,还要再做一次,这样最劣要从前往后做 \(n\) 次,复杂度 \(O(n^2)\)。
我们发现,当加入一个数时,如果平均值不变小,就可以和上一段比,如果上一段平均值比这一段平均值大,那么往前合并,然后将下一个数当做一个新段。因为每合并一次就会少一段,所以这这样做 \(O(n)\) 的。
瓶颈在排序,复杂度 \(O(nlogn)\)。
T3
看到序列,端点,两个限制条件,
很一眼二维数点。
先用单调栈维护出每一个点向左向右分别最多能够扩展的距离。
以 \(i\) 为左端点,所有的右端点需要满足条件 \(R_i\le j\le n\),\(i\le L_j\)。
所以我们建立二维平面,以 \(i\) 为横坐标,\(L_i\) 为纵坐标询问的区间就是 \([R_i,i,n,n]\)。
右端点的情况下同理。
然而,由于一些玄学问题我这个题用 sort 无法通过,但是用 stable_sort 却可以通过。
?????????

int n,id;
int a[N];
struct Bit{
int tr[M];
inline void add(int x,int val){
if(x==0)return;
for(;x<n+10;x+=x&(-x))tr[x]+=val;
}
inline int sum(int x){
int res=0;
for(;x;x-=x&(-x))res+=tr[x];
return res;
}
inline void init(){
memset(tr,0,sizeof tr);
}
}T;
int s[N],top;
int Lmin[N],Lmax[N],Rmin[N],Rmax[N];
int L[N],R[N];
struct node{
int op,x,y,add,id;
}p[N<<1];
int cnt;
inline bool cmp(node aa, node bb){
if (aa.x != bb.x)return aa.x<bb.x;
else if (aa.op != bb.op)return aa.op<bb.op;
return aa.y < bb.y;
}
int ans1[N],ans2[N];
inline void solve2(){
cnt=0;
up(i,1,n){
p[++cnt]={0,i,R[i]+1,0,0};
}
up(i,1,n){
if(L[i]<1)continue;
p[++cnt]={1,L[i],i+1,1,i};
}
stable_sort(p+1,p+1+cnt,cmp);
T.init();
up(i,1,cnt){
if (!p[i].op)T.add(p[i].y,1);
else {
ans2[p[i].id]+=p[i].add*T.sum(p[i].y);
}
}
}
signed main(){
freopen("interval.in","r",stdin);
freopen("interval.out","w",stdout);
n=read();id=read();
up(i,1,n){
a[i]=read();
}
top=0;
up(i,1,n){
while(top&&a[s[top]]<=a[i])top--;
Lmax[i]=s[top];
s[++top]=i;
}
top=0;
up(i,1,n){
while(top&&a[s[top]]>=a[i])top--;
Lmin[i]=s[top];
s[++top]=i;
}
up(i,1,n)L[i]=min(Lmax[i],Lmin[i]);
top=0;
s[top]=n+1;
dn(i,n,1){
while(top&&a[s[top]]<=a[i])top--;
Rmax[i]=s[top];
s[++top]=i;
}
top=0;
s[top]=n+1;
dn(i,n,1){
while(top&&a[s[top]]>=a[i])top--;
Rmin[i]=s[top];
s[++top]=i;
}
up(i,1,n)R[i]=max(Rmax[i],Rmin[i]);
solve2();
cnt=0;
up(i,1,n){
p[++cnt]={0,n+1-i,n+1-L[i]+1,0,0};
if(n+1-R[i]<1)continue;
p[++cnt]={1,n+1-R[i],n+1-i+1,1,i};
}
stable_sort(p+1,p+1+cnt,cmp);
T.init();
up(i,1,cnt){
if (!p[i].op)T.add(p[i].y,1);
else ans1[p[i].id]+=p[i].add*T.sum(p[i].y);
}
up(i,1,n){
write(ans1[i],0);
}
printf("\n");
up(i,1,n){
write(ans2[i],0);
}
return 0;
}

梦熊_炼石计划
浙公网安备 33010602011771号