Codeforces 1150

1150 C

题意

给你一个由 \(1,2\) 组成的数组,要你重新排列这个数组,使得它的所有是质数的前缀和最长。 \((1\le n\le 200000)\)

Examples

input
5
1 2 1 2 1
output
1 1 1 2 2
input
9
1 1 2 1 1 1 2 1 1
output
1 1 1 2 1 1 1 2 1

一个一个往上去凑就行了,优先选2。

1150 D

题意

给你一个长 \(\le 10^5\) 的文本串,现在你有三个模式串,每个长度 \(\le 250\) ,初始为空,现在你有两种操作:

  • + i c,给串 \(i\) 末尾加上一个字符 \(c\)
  • - i,删除 \(i\) 末尾的字符

现在在每次操作后询问三个串是否分别对应文本串中三个不相交的子序列

Examples

input

6 8
abdabc
+ 1 a
+ 1 d
+ 2 b
+ 2 c
+ 3 a
+ 3 b
+ 1 c
- 2

output

YES
YES
YES
YES
YES
YES
NO
YES

input

6 8
abbaab
+ 1 a
+ 2 a
+ 3 a
+ 1 b
+ 2 b
+ 3 b
- 1
+ 2 z

output

YES
YES
YES
YES
YES
NO
YES
NO

每次询问可以在 \(O(len^2)(len\le 250)\) 时间内解决,考虑 \(n^2\) dp。
我们设 \(nxt[i][c]\) 为字符 \(c\) 在位置 \(i\) 后第一次出现的位置。
先考虑 \(n^3\) 做法。
\(dp[i][j][k]\) 表示第一个串匹配到 \(i\) ,第二个串匹配到 \(j\) ,第三个串匹配到 \(k\) ,三个串对应的子序列中末尾最前面的末尾的位置。
这样的话全部转移是 \(n^3\) 的。
但是对于每个询问我们发现只需要用 \(n^2\) 去更新对应的dp值。
所以复杂度为 \(O(qlen^2)\)

1150 E

题意

给你一个括号序列,有 \(q(\le 10^5)\) 次操作,每次操作交换两个括号,问每一步操作后括号序列对应的括号树的直径。

Examples

input
5 5
(((())))
4 5
3 4
5 6
3 6
2 5
output
4
3
3
2
4
4
input
6 4
(((())()))
6 7
5 4
6 4
7 4
output
4
4
4
5
3

线段树维护括号区间。
树的直径其实就是 \(\max(dep[u]+dep[v]-2*dep[lca(u,v)])\)
对于每一个区间,我们记

  • \(delta\) 最左侧括号深度 \(-\) 最右侧括号深度。
  • \(mx\)\(dep[u]\) ,最深的节点的深度
  • \(m\) 即 $\(dep[lca]\) ,最浅的节点的深度
  • \(lm\)\(dep[u]-2*dep[lca]\)
  • \(mr\)\(-2*dep[lca]+dep[v]\)
  • \(lmr\)\(dep[u]+dep[v]-2*dep[lca]\)

询问时询问线段树根节点的 \(lmr\) 值。

Code

#include<bits/stdc++.h>
using namespace std;
const int maxn=200003,INF=1050000000;
struct node{int delta,mx,m,lm,mr,lmr;}t[maxn<<2];
int n;
char s[maxn];
node operator +(node t2,node t3){
    node t1;
    t1.delta=t2.delta+t3.delta;
    t3.mx+=t2.delta;
    t3.m-=2*t2.delta;
    t3.lm-=t2.delta;
    t3.mr-=t2.delta;
    t1.mx=max(t2.mx,t3.mx);
    t1.m=max(t2.m,t3.m);
    t1.lm=max(max(t2.lm,t3.lm),t2.mx+t3.m);
    t1.mr=max(max(t2.mr,t3.mr),t2.m+t3.mx);
    t1.lmr=max(max(t2.lmr,t3.lmr),max(t2.mx+t3.mr,t2.lm+t3.mx));
    return t1;
}
void build(int p,int l,int r){
    if(l==r){
        t[p].mx=t[p].delta=(s[l]=='('?1:-1);
        t[p].m=-2*t[p].mx;
        t[p].lm=t[p].mr=-INF;
        t[p].lmr=-INF;
        return;
    }
    int mid=(l+r)>>1;
    build(p<<1,l,mid);
    build(p<<1|1,mid+1,r);
    t[p]=t[p<<1]+t[p<<1|1];
}
void change(int p,int l,int r,int pos,char k){
    if(l==r){
        int f=(k=='('?2:-2);
        t[p].delta+=f;
        t[p].mx+=f;
        t[p].m-=2*f;
        t[p].lm-=f;
        t[p].mr-=f;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid)change(p<<1,l,mid,pos,k);
    else change(p<<1|1,mid+1,r,pos,k);
    t[p]=t[p<<1]+t[p<<1|1];
}
int query(){
    return max(t[1].mx,t[1].lmr);
}
int main(){
    int Q;
    scanf("%d%d%s",&n,&Q,s+1);
    n=(n-1)<<1;
    build(1,1,n);
    printf("%d\n",query());
    while(Q--){
        int x,y;
        scanf("%d%d",&x,&y);
        change(1,1,n,x,s[x]=(s[x]=='('?')':'('));
        change(1,1,n,y,s[y]=(s[y]=='('?')':'('));
        printf("%d\n",query());
    }
    return 0;
}

转载于:https://www.cnblogs.com/BlogOfchc1234567890/p/10807485.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值